Goodbye bacon!

This commit is contained in:
Geoffrey Frogeye 2018-10-06 10:27:36 +02:00
parent d010c48306
commit 4f02db850b
15 changed files with 423 additions and 204 deletions

View File

@ -5,5 +5,4 @@ echo $(for i in "" "%20Bold" "%20Oblique" "%20Bold%20Oblique"; do
cd $HOME/.local/share/fonts cd $HOME/.local/share/fonts
wget -c http://raw.githubusercontent.com/powerline/fonts/master/DejaVuSansMono/DejaVu%20Sans%20Mono$i%20for%20Powerline.ttf wget -c http://raw.githubusercontent.com/powerline/fonts/master/DejaVuSansMono/DejaVu%20Sans%20Mono$i%20for%20Powerline.ttf
done) done)
wget -c "https://aur.archlinux.org/cgit/aur.git/plain/icons.ttf?h=ttf-font-icons" -O $HOME/.local/share/fonts/icons.ttf wget -c "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" -O $HOME/.local/share/fonts/fontawesome-webfont.ttf

View File

@ -64,8 +64,8 @@ bindsym $mod+F11 exec urxvtc -e 'pacmixer'
bindsym $mod+F12 exec urxvtc -e 'pacmixer' bindsym $mod+F12 exec urxvtc -e 'pacmixer'
#Brightness control #Brightness control
bindsym XF86MonBrightnessDown exec xbacklight -dec 20 bindsym XF86MonBrightnessDown exec xbacklight -dec 20 -time 0
bindsym XF86MonBrightnessUp exec xbacklight -inc 20 bindsym XF86MonBrightnessUp exec xbacklight -inc 20 -time 0
# Screenshots # Screenshots
bindsym Print exec scrot -ue 'mv $f ~/Screenshots/' bindsym Print exec scrot -ue 'mv $f ~/Screenshots/'
@ -226,13 +226,6 @@ bindsym $mod+ctrl+shift+Left move workspace to output left
bindsym $mod+Ctrl+Shift+Up move workspace to output above bindsym $mod+Ctrl+Shift+Up move workspace to output above
bindsym $mod+Ctrl+Shift+Down move workspace to output below bindsym $mod+Ctrl+Shift+Down move workspace to output below
# Open applications on specific workspaces
assign [class="Thunderbird"] $WS7
assign [class="Skype"] $WS7
assign [class="Pidgin"] $WS7
assign [class="Clementine"] $WS10
assign [title="TweetDeck"] $WS8
# Open specific applications in floating mode # Open specific applications in floating mode
for_window [title="pacmixer"] floating enable border pixel 2 for_window [title="pacmixer"] floating enable border pixel 2
for_window [class="Firefox"] layout tabbed for_window [class="Firefox"] layout tabbed
@ -353,6 +346,7 @@ set_from_resource $color15 i3wm.color15 #cfd0c2
# Inactivity settings # Inactivity settings
exec --no-startup-id xautolock -time 10 -locker 'xset dpms force standby' -killtime 1 -killer '$locker' exec --no-startup-id xautolock -time 10 -locker 'xset dpms force standby' -killtime 1 -killer '$locker'
bindsym $mod+F1 exec --no-startup-id xset dpms force off
bindsym $mod+F4 exec --no-startup-id xautolock -disable bindsym $mod+F4 exec --no-startup-id xautolock -disable
bindsym $mod+F5 exec --no-startup-id xautolock -enable bindsym $mod+F5 exec --no-startup-id xautolock -enable
@ -366,7 +360,7 @@ exec --no-startup-id unclutter -root # Hide mouse cursor after some time
#exec --no-startup-id dunst # Notifications (handled by systemd) #exec --no-startup-id dunst # Notifications (handled by systemd)
exec --no-startup-id keynav # Keyboard cursor controller exec --no-startup-id keynav # Keyboard cursor controller
#exec --no-startup-id mpd # Music Player Daemon (handled by systemd) #exec --no-startup-id mpd # Music Player Daemon (handled by systemd)
exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill # exec --no-startup-id ~/.config/i3/ashuffle # MPD Auto-refill
exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it exec --no-startup-id autorandr --change # Screen configuration and everything that depends on it
exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification exec --no-startup-id ~/.config/i3/batteryNotify -d # Battery state notification
@ -381,3 +375,7 @@ client.urgent $color01 $color01 $color07 $foreground $color09
client.placeholder $ignore $color06 $color07 $ignore $color14 client.placeholder $ignore $color06 $color07 $ignore $color14
client.background $color15 client.background $color15
# bar {
# i3bar_command ~/.config/lemonbar/bar.py
# }

View File

@ -25,6 +25,7 @@ if __name__ == "__main__":
Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT) Bar.addSectionAll(MpdProvider(theme=7), BarGroupType.LEFT)
# Bar.addSectionAll(I3WindowTitleProvider(), BarGroupType.LEFT) # Bar.addSectionAll(I3WindowTitleProvider(), BarGroupType.LEFT)
# TODO Computer modes
SYSTEM_THEME = 2 SYSTEM_THEME = 2
DANGER_THEME = FOCUS_THEME DANGER_THEME = FOCUS_THEME
@ -40,6 +41,7 @@ if __name__ == "__main__":
# TODO Disk space provider # TODO Disk space provider
# TODO Screen (connected, autorandr configuration, bbswitch) provider # TODO Screen (connected, autorandr configuration, bbswitch) provider
Bar.addSectionAll(PulseaudioProvider(theme=PERIPHERAL_THEME), BarGroupType.RIGHT) Bar.addSectionAll(PulseaudioProvider(theme=PERIPHERAL_THEME), BarGroupType.RIGHT)
Bar.addSectionAll(RfkillProvider(theme=PERIPHERAL_THEME), BarGroupType.RIGHT)
Bar.addSectionAll(NetworkProvider(theme=NETWORK_THEME), BarGroupType.RIGHT) Bar.addSectionAll(NetworkProvider(theme=NETWORK_THEME), BarGroupType.RIGHT)
# Personal # Personal
@ -50,3 +52,5 @@ if __name__ == "__main__":
TIME_THEME = 6 TIME_THEME = 6
Bar.addSectionAll(TimeProvider(theme=TIME_THEME), BarGroupType.RIGHT) Bar.addSectionAll(TimeProvider(theme=TIME_THEME), BarGroupType.RIGHT)
# Bar.run()

View File

@ -3,6 +3,9 @@
import enum import enum
import threading import threading
import time import time
import i3ipc
import os
import signal
import subprocess import subprocess
import logging import logging
import coloredlogs import coloredlogs
@ -10,6 +13,7 @@ import coloredlogs
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger() log = logging.getLogger()
# TODO Allow deletion of Bar, BarGroup and Section for screen changes # TODO Allow deletion of Bar, BarGroup and Section for screen changes
# IDEA Use i3 ipc events rather than relying on xrandr or Xlib (less portable # IDEA Use i3 ipc events rather than relying on xrandr or Xlib (less portable
# but easier) # but easier)
@ -18,6 +22,7 @@ log = logging.getLogger()
# TODO Use bytes rather than strings # TODO Use bytes rather than strings
# TODO Use default colors of lemonbar sometimes # TODO Use default colors of lemonbar sometimes
# TODO Adapt bar height with font height # TODO Adapt bar height with font height
# TODO OPTI Static text objects that update its parents if modified
class BarGroupType(enum.Enum): class BarGroupType(enum.Enum):
@ -30,7 +35,7 @@ class BarGroupType(enum.Enum):
class BarStdoutThread(threading.Thread): class BarStdoutThread(threading.Thread):
def run(self): def run(self):
while True: while Bar.running:
handle = Bar.process.stdout.readline().strip() handle = Bar.process.stdout.readline().strip()
if not len(handle): if not len(handle):
continue continue
@ -52,6 +57,7 @@ class Bar:
@staticmethod @staticmethod
def init(): def init():
Bar.running = True
Section.init() Section.init()
cmd = ['lemonbar', '-b', '-a', '64'] cmd = ['lemonbar', '-b', '-a', '64']
@ -66,10 +72,35 @@ class Bar:
Bar(0) Bar(0)
# Bar(1) # Bar(1)
@staticmethod
def stop():
Bar.running = False
Bar.process.kill()
# TODO This is not really the best way to do it I guess
os.killpg(os.getpid(), signal.SIGTERM)
@staticmethod
def run():
Bar.forever()
i3 = i3ipc.Connection()
def doStop(*args):
Bar.stop()
print(88)
try:
i3.on('ipc_shutdown', doStop)
i3.main()
except BaseException:
print(93)
Bar.stop()
# Class globals # Class globals
everyone = set() everyone = set()
string = "" string = ""
process = None process = None
running = False
nextHandle = 0 nextHandle = 0
actionsF2H = dict() actionsF2H = dict()
@ -92,8 +123,11 @@ class Bar:
@staticmethod @staticmethod
def forever(): def forever():
while True: try:
time.sleep(60) while True:
time.sleep(60)
except BaseException:
Bar.stop()
def __init__(self, screen): def __init__(self, screen):
assert isinstance(screen, int) assert isinstance(screen, int)
@ -136,15 +170,16 @@ class Bar:
@staticmethod @staticmethod
def updateAll(): def updateAll():
Bar.string = "" if Bar.running:
for bar in Bar.everyone: Bar.string = ""
bar.update() for bar in Bar.everyone:
Bar.string += bar.string bar.update()
# Color for empty sections Bar.string += bar.string
Bar.string += BarGroup.color(*Section.EMPTY) # Color for empty sections
Bar.string += BarGroup.color(*Section.EMPTY)
Bar.process.stdin.write(bytes(Bar.string + '\n', 'utf-8')) Bar.process.stdin.write(bytes(Bar.string + '\n', 'utf-8'))
Bar.process.stdin.flush() Bar.process.stdin.flush()
class BarGroup: class BarGroup:
@ -276,11 +311,14 @@ class SectionThread(threading.Thread):
class Section: class Section:
# TODO Update all of that to base16 # TODO Update all of that to base16
COLORS = ['#002b36', '#dc322f', '#859900', '#b58900', '#268bd2', '#6c71c4', # COLORS = ['#272822', '#383830', '#49483e', '#75715e', '#a59f85', '#f8f8f2',
'#2aa198', '#93a1a1', '#657b83', '#dc322f', '#859900', '#b58900', # '#f5f4f1', '#f9f8f5', '#f92672', '#fd971f', '#f4bf75', '#a6e22e',
'#268bd2', '#6c71c4', '#2aa198', '#fdf6e3'] # '#a1efe4', '#66d9ef', '#ae81ff', '#cc6633']
FGCOLOR = '#93a1a1' COLORS = ['#181818', '#AB4642', '#A1B56C', '#F7CA88', '#7CAFC2', '#BA8BAF',
BGCOLOR = '#002b36' '#86C1B9', '#D8D8D8', '#585858', '#AB4642', '#A1B56C', '#F7CA88',
'#7CAFC2', '#BA8BAF', '#86C1B9', '#F8F8F8']
FGCOLOR = '#F8F8F2'
BGCOLOR = '#272822'
THEMES = list() THEMES = list()
EMPTY = (FGCOLOR, BGCOLOR) EMPTY = (FGCOLOR, BGCOLOR)
@ -357,6 +395,8 @@ class Section:
def updateText(self, text): def updateText(self, text):
if isinstance(text, str): if isinstance(text, str):
text = Text(text) text = Text(text)
elif isinstance(text, Text) and not len(text.elements):
text = None
self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ') self.dstText[0] = None if (text is None and not self.persistent) else ((' ' + self.icon + ' ') if self.icon else ' ')
self.dstText[1] = text self.dstText[1] = text
@ -373,6 +413,12 @@ class Section:
Section.sizeChanging.add(self) Section.sizeChanging.add(self)
Section.somethingChanged.set() Section.somethingChanged.set()
def setDecorators(self, **kwargs):
self.dstText.setDecorators(**kwargs)
self.curText = str(self.dstText)
self.informParentsTextChanged()
Section.somethingChanged.set()
def updateTheme(self, theme): def updateTheme(self, theme):
assert isinstance(theme, int) assert isinstance(theme, int)
assert theme < len(Section.THEMES) assert theme < len(Section.THEMES)
@ -440,13 +486,14 @@ class Section:
class StatefulSection(Section): class StatefulSection(Section):
# TODO FEAT Allow to temporary expand the section (e.g. when important change) # TODO FEAT Allow to temporary expand the section (e.g. when important change)
NUMBER_STATES = None NUMBER_STATES = None
DEFAULT_STATE = 0
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
Section.__init__(self, *args, **kwargs) Section.__init__(self, *args, **kwargs)
self.state = 0 self.state = self.DEFAULT_STATE
if hasattr(self, 'onChangeState'): if hasattr(self, 'onChangeState'):
self.onChangeState(self.state) self.onChangeState(self.state)
self.dstText.setDecorators(clickLeft=self.incrementState, self.setDecorators(clickLeft=self.incrementState,
clickRight=self.decrementState) clickRight=self.decrementState)
def incrementState(self): def incrementState(self):
@ -466,6 +513,9 @@ class StatefulSection(Section):
self.refreshData() self.refreshData()
class ColorCountsSection(StatefulSection): class ColorCountsSection(StatefulSection):
# TODO FEAT Blend colors when not expanded
# TODO FEAT Blend colors with importance of count
# TODO FEAT Allow icons instead of counts
NUMBER_STATES = 3 NUMBER_STATES = 3
COLORABLE_ICON = '?' COLORABLE_ICON = '?'
@ -552,6 +602,8 @@ class Text:
nest('A' + number + ':' + handle.decode() + ':', 'A' + number) nest('A' + number + ':' + handle.decode() + ':', 'A' + number)
for key, val in self.decorators.items(): for key, val in self.decorators.items():
if val is None:
continue
if key == 'fg': if key == 'fg':
reset = self.section.THEMES[self.section.theme][0] reset = self.section.THEMES[self.section.theme][0]
nest('F' + getColor(val), 'F' + reset) nest('F' + getColor(val), 'F' + reset)

15
config/lemonbar/launch.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env sh
# TODO Make this better
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
ex="$DIR/bar.py"
# Terminate already running bar instances
ps -af | grep "python3 $ex" | grep -v grep | awk '{print $2}' | while read p; do kill $p; done
killall -q lemonbar
$ex

View File

@ -18,6 +18,8 @@ import random
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s') coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger() log = logging.getLogger()
# TODO Generator class (for I3WorkspacesProvider, NetworkProvider and later
# PulseaudioProvider and MpdProvider)
def humanSize(num): def humanSize(num):
""" """
@ -43,6 +45,7 @@ class TimeProvider(StatefulSection, PeriodicUpdater):
"%d/%m %H:%M:%S", "%d/%m %H:%M:%S",
"%a %d/%m/%y %H:%M:%S"] "%a %d/%m/%y %H:%M:%S"]
NUMBER_STATES = len(FORMATS) NUMBER_STATES = len(FORMATS)
DEFAULT_STATE = 1
def fetcher(self): def fetcher(self):
now = datetime.datetime.now() now = datetime.datetime.now()
@ -114,13 +117,13 @@ class RamProvider(AlertingSection, PeriodicUpdater):
def fetcher(self): def fetcher(self):
mem = psutil.virtual_memory() mem = psutil.virtual_memory()
freePerc = 1-mem.percent/100 freePerc = mem.percent/100
self.updateLevel(freePerc) self.updateLevel(freePerc)
if self.state < 1: if self.state < 1:
return None return None
text = Text(Section.ramp(freePerc)) text = Text(Section.ramp(1-freePerc))
if self.state >= 2: if self.state >= 2:
freeStr = humanSize(mem.available) freeStr = humanSize(mem.available)
text.append(freeStr) text.append(freeStr)
@ -162,7 +165,8 @@ class TemperatureProvider(AlertingSection, PeriodicUpdater):
class BatteryProvider(AlertingSection, PeriodicUpdater): class BatteryProvider(AlertingSection, PeriodicUpdater):
NUMBER_STATES = 2 # TODO Support ACPID for events
NUMBER_STATES = 3
RAMP = "" RAMP = ""
def fetcher(self): def fetcher(self):
@ -179,8 +183,15 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
if self.state < 1: if self.state < 1:
return return
return Text('{:.0f}%'.format(bat.percent)) t = Text('{:.0f}%'.format(bat.percent))
# TODO Time remaining (if the estimation is somewhat correct)
if self.state < 2:
return t
h = int(bat.secsleft / 3600)
m = int((bat.secsleft - h * 3600) / 60)
t.append(" ({:d}:{:02d})".format(h, m))
return t
def __init__(self, theme=None): def __init__(self, theme=None):
AlertingSection.__init__(self, theme) AlertingSection.__init__(self, theme)
@ -189,10 +200,13 @@ class BatteryProvider(AlertingSection, PeriodicUpdater):
class PulseaudioProvider(Section, ThreadedUpdater): class PulseaudioProvider(StatefulSection, ThreadedUpdater):
NUMBER_STATES = 3
DEFAULT_STATE = 1
def __init__(self, theme=None): def __init__(self, theme=None):
ThreadedUpdater.__init__(self) ThreadedUpdater.__init__(self)
Section.__init__(self, theme) StatefulSection.__init__(self, theme)
self.pulseEvents = pulsectl.Pulse('event-handler') self.pulseEvents = pulsectl.Pulse('event-handler')
self.pulseEvents.event_mask_set(pulsectl.PulseEventMaskEnum.sink) self.pulseEvents.event_mask_set(pulsectl.PulseEventMaskEnum.sink)
@ -205,20 +219,32 @@ class PulseaudioProvider(Section, ThreadedUpdater):
sinks = [] sinks = []
with pulsectl.Pulse('list-sinks') as pulse: with pulsectl.Pulse('list-sinks') as pulse:
for sink in pulse.sink_list(): for sink in pulse.sink_list():
vol = pulse.volume_get_all_chans(sink)
if vol > 1:
vol = 1
if sink.port_active.name == "analog-output-headphones": if sink.port_active.name == "analog-output-headphones":
icon = "" icon = ""
elif sink.port_active.name == "analog-output-speaker": elif sink.port_active.name == "analog-output-speaker":
icon = "" icon = "" if sink.mute else ""
else: else:
icon = "?" icon = "?"
vol = pulse.volume_get_all_chans(sink)
fg = (sink.mute and '#333333') or (vol > 1 and '#FF0000') or None
ramp = "" if sink.mute else (" " + self.ramp(vol)) t = Text(icon, fg=fg)
sinks.append(icon + ramp) sinks.append(t)
return " ".join(sinks)
if self.state < 1:
continue
if self.state < 2:
if not sink.mute:
ramp = " "
while vol >= 0:
ramp += self.ramp(vol if vol < 1 else 1)
vol -= 1
t.append(ramp)
else:
t.append(" {:2.0f}%".format(vol*100))
return Text(*sinks)
def loop(self): def loop(self):
self.pulseEvents.event_listen() self.pulseEvents.event_listen()
@ -230,6 +256,7 @@ class PulseaudioProvider(Section, ThreadedUpdater):
class NetworkProviderSection(StatefulSection, Updater): class NetworkProviderSection(StatefulSection, Updater):
NUMBER_STATES = 5 NUMBER_STATES = 5
DEFAULT_STATE = 1
def actType(self): def actType(self):
self.ssid = None self.ssid = None
@ -260,6 +287,8 @@ class NetworkProviderSection(StatefulSection, Updater):
return ipv4, ipv6 return ipv4, ipv6
def fetcher(self): def fetcher(self):
self.icon = None
self.persistent = False
if self.iface not in self.parent.stats or \ if self.iface not in self.parent.stats or \
not self.parent.stats[self.iface].isup or \ not self.parent.stats[self.iface].isup or \
self.iface.startswith('lo'): self.iface.startswith('lo'):
@ -364,6 +393,41 @@ class NetworkProvider(Section, PeriodicUpdater):
self.fetchData() self.fetchData()
self.changeInterval(5) self.changeInterval(5)
class RfkillProvider(Section, PeriodicUpdater):
# TODO FEAT rfkill doesn't seem to indicate that the hardware switch is
# toggled
PATH = '/sys/class/rfkill'
def fetcher(self):
t = Text()
for device in os.listdir(self.PATH):
with open(os.path.join(self.PATH, device, 'soft'), 'rb') as f:
softBlocked = f.read().strip() != b'0'
with open(os.path.join(self.PATH, device, 'hard'), 'rb') as f:
hardBlocked = f.read().strip() != b'0'
if not hardBlocked and not softBlocked:
continue
with open(os.path.join(self.PATH, device, 'type'), 'rb') as f:
typ = f.read().strip()
fg = (hardBlocked and '#CCCCCC') or (softBlocked and '#FF0000')
if typ == b'wlan':
icon = ''
elif typ == b'bluetooth':
icon = ''
else:
icon = '?'
t.append(Text(icon, fg=fg))
return t
def __init__(self, theme=None):
PeriodicUpdater.__init__(self)
Section.__init__(self, theme)
self.changeInterval(5)
class SshAgentProvider(PeriodicUpdater): class SshAgentProvider(PeriodicUpdater):
def fetcher(self): def fetcher(self):
cmd = ["ssh-add", "-l"] cmd = ["ssh-add", "-l"]
@ -405,6 +469,7 @@ class GpgAgentProvider(PeriodicUpdater):
self.changeInterval(5) self.changeInterval(5)
class KeystoreProvider(Section, MergedUpdater): class KeystoreProvider(Section, MergedUpdater):
# TODO OPTI+FEAT Use ColorCountsSection and not MergedUpdater, this is useless
ICON = '' ICON = ''
def __init__(self, theme=None): def __init__(self, theme=None):
@ -412,8 +477,6 @@ class KeystoreProvider(Section, MergedUpdater):
Section.__init__(self, theme) Section.__init__(self, theme)
class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater): class NotmuchUnreadProvider(ColorCountsSection, InotifyUpdater):
# TODO OPTI Transform InotifyUpdater (watching notmuch folder should be
# enough)
COLORABLE_ICON = '' COLORABLE_ICON = ''
def subfetcher(self): def subfetcher(self):
@ -456,7 +519,6 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
COLORABLE_ICON = '' COLORABLE_ICON = ''
def updateCalendarList(self): def updateCalendarList(self):
print(459)
calendars = sorted(os.listdir(self.dir)) calendars = sorted(os.listdir(self.dir))
for calendar in calendars: for calendar in calendars:
# If the calendar wasn't in the list # If the calendar wasn't in the list
@ -473,7 +535,6 @@ class TodoProvider(ColorCountsSection, InotifyUpdater):
with open(path, 'r') as f: with open(path, 'r') as f:
self.colors[calendar] = f.read().strip() self.colors[calendar] = f.read().strip()
self.calendars = calendars self.calendars = calendars
print(475, self.calendars)
def __init__(self, dir, theme=None): def __init__(self, dir, theme=None):
""" """
@ -539,6 +600,9 @@ class I3WorkspacesProviderSection(Section):
else: else:
return self.parent.themeNormal return self.parent.themeNormal
# TODO On mode change the state (shown / hidden) gets overriden so every
# tab is shown
def show(self): def show(self):
self.updateTheme(self.selectTheme()) self.updateTheme(self.selectTheme())
self.updateText(self.fullName if self.focused else self.shortName) self.updateText(self.fullName if self.focused else self.shortName)
@ -553,10 +617,15 @@ class I3WorkspacesProviderSection(Section):
self.fullName = self.parent.customNames[name] \ self.fullName = self.parent.customNames[name] \
if name in self.parent.customNames else name if name in self.parent.customNames else name
def switchTo(self):
self.parent.i3.command('workspace {}'.format(self.shortName))
def __init__(self, name, parent): def __init__(self, name, parent):
Section.__init__(self) Section.__init__(self)
self.parent = parent self.parent = parent
self.setName(name) self.setName(name)
self.setDecorators(clickLeft=self.switchTo)
def empty(self): def empty(self):
self.updateTheme(self.parent.themeNormal) self.updateTheme(self.parent.themeNormal)
@ -564,7 +633,7 @@ class I3WorkspacesProviderSection(Section):
class I3WorkspacesProvider(Section, I3Updater): class I3WorkspacesProvider(Section, I3Updater):
# TODO Multi-screen # TODO FEAT Multi-screen
def initialPopulation(self, parent): def initialPopulation(self, parent):
""" """
@ -674,10 +743,13 @@ class MpdProvider(Section, ThreadedUpdater):
self.start() self.start()
def fetcher(self): def fetcher(self):
cur = self.mpd.currentsong() stat = self.mpd.status()
if not len(stat) or stat["state"] == "stop":
return None
cur = self.mpd.currentsong()
if not len(cur): if not len(cur):
return '' return None
infos = [] infos = []

View File

@ -190,10 +190,16 @@ class ThreadedUpdaterThread(threading.Thread):
def __init__(self, updater, *args, **kwargs): def __init__(self, updater, *args, **kwargs):
self.updater = updater self.updater = updater
threading.Thread.__init__(self, *args, **kwargs) threading.Thread.__init__(self, *args, **kwargs)
self.looping = True
def run(self): def run(self):
while True: try:
self.updater.loop() while self.looping:
self.updater.loop()
except BaseException as e:
log.error("Error with {}".format(self.updater))
log.error(e, exc_info=True)
self.updater.updateText("")
class ThreadedUpdater(Updater): class ThreadedUpdater(Updater):

View File

@ -76,7 +76,8 @@ enable-ipc = true
inherit = bar/base inherit = bar/base
modules-center = mpd modules-center = mpd
modules-right = cpu memory temperature mail todo vpncheck eth wlan bbswitch xbacklight volume battery shortdate ; modules-right = cpu memory temperature mail todo vpncheck eth wlan bbswitch xbacklight volume battery shortdate
modules-right = cpu memory temperature mail vpncheck eth wlan bbswitch xbacklight volume battery shortdate
tray-position = right tray-position = right
tray-padding = 2 tray-padding = 2

View File

@ -1 +0,0 @@
/home/geoffrey/.local/share/systemd/user/mbsync.timer

View File

@ -1 +0,0 @@
/usr/lib/systemd/user/syncthing.service

View File

@ -1,31 +1,139 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import argparse import argparse
import coloredlogs
import logging
import os
import sys
parser = argparse.ArgumentParser(description="Place a folder in ~/Documents in ~/Documents/Archives and symlink it") coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
parser.add_argument('dir', metavar='DIRECTORY', type=str, help="The directory to archive") log = logging.getLogger()
parser.add_argument('-d', '--dry', action='store_true')
args = parser.parse_args() # Coding conventions:
# No leading or trailing slashes. Let os.path.join do its job
# TODO Config arparse and pass args to the functions. No globals
# Finding directories # Finding directories
assert('HOME' in os.environ), "Home directory unknown" assert 'HOME' in os.environ, "Home directory unknown"
docs = os.path.realpath(os.path.join(os.environ['HOME'], 'Documents')) DOCS = os.path.realpath(os.path.join(os.environ['HOME'], 'Documents'))
assert(os.path.isdir(docs)), "Documents folder not found" assert os.path.isdir(DOCS), "Documents folder not found"
arcs = os.path.join(docs, 'Archives') ARCS = os.path.realpath(os.path.join(os.environ['HOME'], 'Archives'))
assert(os.path.isdir(arcs)), "Archives folder not found" assert os.path.isdir(ARCS), "Archives folder not found"
def dirRange(relpath):
splits = relpath.split(os.path.sep)
res = list()
for p in range(len(splits)):
partPath = os.path.join(*splits[:p+1])
arcPath = os.path.join(os.path.join(ARCS, partPath))
docPath = os.path.join(os.path.join(DOCS, partPath))
res.append((docPath, arcPath))
return res
def travel(relpath):
"""
Dunno what this will do, let's write code and see.
"""
wholeRange = dirRange(relpath)
for tup in wholeRange:
isLast = wholeRange[-1] == tup
docPath, arcPath = tup
linkPath = os.path.relpath(arcPath, start=docPath)
log.debug(f"47 {tup}")
if not os.path.exists(docPath) and not os.path.exists(arcPath):
log.error("Not existing")
sys.exit(1)
elif os.path.isdir(docPath) and os.path.isdir(arcPath) and not isLast:
log.debug("Both folder")
continue
elif os.path.isdir(docPath) and os.path.isdir(arcPath) and isLast:
log.error("This should fail for some reason, maybe")
sys.exit(1)
elif os.path.islink(docPath) and os.path.exists(arcPath):
currentLink = os.readlink(docPath)
if currentLink != linkPath:
log.warning(f"'{docPath}' is pointing to '{currentLink}' " +
f"but should point to '{linkPath}'.")
# TODO Fixing if asked for
sys.exit(1)
log.debug("Early link already exists {docPath} → {arcPath}")
return
elif not os.path.exists(docPath) and os.path.exists(arcPath):
log.debug("Only existing on archive side, linking")
print(f"ln -s {linkPath} {docPath}")
elif os.path.exists(docPath) and not os.path.exists(arcPath) \
and isLast:
log.debug("Only existing on doc side, moving and linking")
print(f"mv {docPath} {arcPath}")
print(f"ln -s {linkPath} {docPath}")
elif os.path.exists(docPath) and not os.path.exists(arcPath) \
and not isLast:
raise NotImplementedError("Here comes the trouble")
else:
log.error("Unhandled case")
sys.exit(1)
def ensureLink(relpath):
"""
Ensure that ~/Documents/$relpath points to ~/Archives/$relpath
"""
arcPath = os.path.join(os.path.join(ARCS, relpath))
docPath = os.path.join(os.path.join(DOCS, relpath))
assert os.path.exists(arcPath)
# For each tree element of the path
for docPath, arcPath in dirRange(relpath):
linkPath = os.path.relpath(arcPath, start=docPath)
def installLink():
if args.dry:
print(f"ln -s {linkPath} {docPath}")
else:
os.symlink(linkPath, docPath)
if os.path.islink(docPath):
currentLink = os.readlink(docPath)
if currentLink != linkPath:
log.warning(f"'{docPath}' is pointing to '{currentLink}' " +
f"but should point to '{linkPath}'. Fixing")
if args.dry:
print(f"rm {docPath}")
else:
os.unlink(docPath)
installLink()
return
elif not os.path.exists(docPath):
installLink()
return
elif os.path.isdir(docPath):
continue
else:
raise RuntimeError(f"'{docPath}' exists and is not a directory " +
f"or a link. Unable to link it to '{linkPath}'")
raise RuntimeError(f"'{docPath}' is a directory. Unable to link it to " +
f"'{linkPath}'")
def archive(docdir): def archive(docdir):
docdir = os.path.realpath(args.dir) docdir = os.path.realpath(args.dir)
assert(os.path.isdir(docdir)), docdir + " must be a directory" assert os.path.isdir(docdir), docdir + " must be a directory"
assert(docdir.startswith(docs)), "Directory is not in the document folder" assert docdir.startswith(DOCS), "Directory is not in the document folder"
assert(not docdir.startswith(arcs)), "Directory is already in the archive folder" assert not docdir.startswith(ARCS), "Directory is already in the archive folder"
reldir = os.path.relpath(docdir, docs) reldir = os.path.relpath(docdir, DOCS)
print("ARC", reldir) print("ARC", reldir)
arcdir = os.path.join(arcs, reldir) arcdir = os.path.join(ARCS, reldir)
parentArcdir = os.path.realpath(os.path.join(arcdir, '..')) parentArcdir = os.path.realpath(os.path.join(arcdir, '..'))
parentDocdir = os.path.realpath(os.path.join(docdir, '..')) parentDocdir = os.path.realpath(os.path.join(docdir, '..'))
linkDest = os.path.relpath(arcdir, parentDocdir) linkDest = os.path.relpath(arcdir, parentDocdir)
@ -35,9 +143,9 @@ def archive(docdir):
# If the directory exists # If the directory exists
if os.path.isdir(arcdir): if os.path.isdir(arcdir):
return return
# for f in os.listdir(arcdir): # for f in os.listdir(arcdir):
# assert(os.path.isdir(f)), "Something unknown in Archive dir") # assert os.path.isdir(f), "Something unknown in Archive dir")
# archive(os.path.join(arcdir, f)) # archive(os.path.join(arcdir, f))
# If the directory doesn't exist, create the directories under it and move all the folder # If the directory doesn't exist, create the directories under it and move all the folder
else: else:
@ -58,8 +166,17 @@ def archive(docdir):
os.symlink(linkDest, docdir) os.symlink(linkDest, docdir)
def unarchive(arcdir): def unarchive(arcdir):
return pass
archive(args.dir)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Place a folder in ~/Documents in ~/Documents/Archives and symlink it")
parser.add_argument('dir', metavar='DIRECTORY', type=str, help="The directory to archive")
parser.add_argument('-d', '--dry', action='store_true')
args = parser.parse_args()
args.dry = True # DEBUG
# archive(args.dir)
ensureLink(args.dir)

View File

@ -21,30 +21,18 @@ function prompt { # text
} }
# Don't ask for things that are already there # Don't ask for things that are already there
if which pacaur &> /dev/null; then if which aurman &> /dev/null; then
PACAUR=1 AURMAN=1
fi fi
if which bauerbill &> /dev/null; then if [ -z $AURMAN ]; then
BAUERBILL=1 prompt "Do you want aurman on this machine?"
AURMAN=$?
fi fi
if [ -z $PACAUR ]; then
prompt "Do you want pacaur on this machine?"
PACAUR=$?
fi
if [ $PACAUR == 1 ]; then
if [ -z $BAUERBILL ]; then
prompt "Do you want bauerbill on this machine?"
BAUERBILL=$?
fi
else
BAUERBILL=0
fi
# COMMON # COMMON
# Install packages if they aren't installed # Install packages if they aren't installed
function inst { function inst {
# Could also use --needed but, meh
for pkg in $*; do for pkg in $*; do
pacman -Q $pkg &> /dev/null pacman -Q $pkg &> /dev/null
if [ $? == 1 ]; then if [ $? == 1 ]; then
@ -69,14 +57,13 @@ inst wget
# Aur # Aur
pacman -Q pacaur &> /dev/null pacman -Q aurman &> /dev/null
if [[ $PACAUR == 1 && $? == 1 ]]; then if [[ $AURMAN == 1 && $? == 1 ]]; then
installPKGBUILD "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=cower" installPKGBUILD "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=aurman"
installPKGBUILD "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=pacaur"
fi fi
# Git for /etc # Git for /etc
sudo pacman -S etckeeper --noconfirm --needed inst etckeeper
sudo etckeeper init sudo etckeeper init
sudo etckeeper commit "~/.dotfiles/scripts/install-arch commit" sudo etckeeper commit "~/.dotfiles/scripts/install-arch commit"
@ -86,25 +73,11 @@ if pacman -Q pamac &> /dev/null ; then
fi fi
# Ccache # Ccache
sudo pacman -S ccache --noconfirm --needed inst ccache
sudo sed 's|BUILDENV=\(.\+\)!ccache\(.\+\)|BUILDENV=\1ccache\2|' /etc/makepkg.conf -i sudo sed 's|BUILDENV=\(.\+\)!ccache\(.\+\)|BUILDENV=\1ccache\2|' /etc/makepkg.conf -i
# Bauerbill
pacman -Q bauerbill &> /dev/null
if [[ $BAUERBILL == 1 && $? == 1 ]]; then
sudo pacman -Sy
gpg --recv-keys 1D1F0DC78F173680
installPKGBUILD http://xyne.archlinux.ca/projects/reflector/pkgbuild/PKGBUILD
pacaur -S bauerbill --noconfirm --noedit
bauerbill -Su --noconfirm
else
sudo pacman -Syu
fi
# TLP # TLP
sudo pacman -S tlp --noconfirm --needed inst tlp
sudo sed 's|SATA_LINKPWR_ON_BAT=min_power|SATA_LINKPWR_ON_BAT=max_performance|' /etc/default/tlp -i sudo sed 's|SATA_LINKPWR_ON_BAT=min_power|SATA_LINKPWR_ON_BAT=max_performance|' /etc/default/tlp -i
sudo systemctl enable tlp.service tlp-sleep.service sudo systemctl enable tlp.service tlp-sleep.service
sudo systemctl disable systemd-rfkill.service systemd-rfkill.socket sudo systemctl disable systemd-rfkill.service systemd-rfkill.socket
@ -117,7 +90,7 @@ echo -e "[Service]\nExecStartPre=/bin/sh -c 'setleds +num < /dev/%I'" | sudo sys
sudo sed "s|#MAKEFLAGS=\"-j2\"|MAKEFLAGS=\"-j$(nproc)\"|" /etc/makepkg.conf -i sudo sed "s|#MAKEFLAGS=\"-j2\"|MAKEFLAGS=\"-j$(nproc)\"|" /etc/makepkg.conf -i
# Time synchronisation # Time synchronisation
sudo pacman -S ntp --noconfirm --needed inst ntp
sudo systemctl start ntpd sudo systemctl start ntpd
sudo systemctl enable ntpd sudo systemctl enable ntpd

View File

@ -54,7 +54,14 @@ if which pacman &> /dev/null; then
function installFileOne { # file function installFileOne { # file
sudo pacman -U "$1" sudo pacman -U "$1"
} }
if which pacaur &> /dev/null; then if which aurman &> /dev/null; then
function altInstallOne { # package
pacman -Q $1 &> /dev/null
if [ $? == 1 ]; then
aurman -S "$1" --noconfirm --noedit
fi
}
elif which pacaur &> /dev/null; then
function altInstallOne { # package function altInstallOne { # package
pacman -Q $1 &> /dev/null pacman -Q $1 &> /dev/null
if [ $? == 1 ]; then if [ $? == 1 ]; then
@ -174,10 +181,11 @@ function systemdUserUnit {
# Common CLI # Common CLI
changeColors monokai
# Utils # Utils
inst coreutils man openssl-tool grep sed sh tar
if [ $TERMUX == 1 ]; then if [ $TERMUX == 1 ]; then
inst coreutils man openssl-tool grep sed sh tar
inst termux-api inst termux-api
if [ $ADMIN == 1 ]; then if [ $ADMIN == 1 ]; then
inst tsu inst tsu
@ -186,17 +194,10 @@ fi
inst moreutils screen ncdu lsof htop proxytunnel pv curl wget netcat mosh bash-completion rsync pwgen fzf highlight inst moreutils screen ncdu lsof htop proxytunnel pv curl wget netcat mosh bash-completion rsync pwgen fzf highlight
# TODO Test those who are on Debian machines and those who aren't # TODO Test those who are on Debian machines and those who aren't
if [ $ARCH == 1 ]; then if [ $ARCH == 1 ]; then
inst bash-completion tldr inst bash-completion
altInst gopass altInst gopass
else else
inst pass inst pass
wget -qO ~/.local/bin/ https://raw.githubusercontent.com/pepa65/tldr-bash-client/master/tldr
chmod +x ~/.local/bin/tldr
fi
tldr -u
if [[ $ARCH == 1 && $ADMIN == 1 ]]; then
inst pkgfile
sudo systemctl enable pkgfile-update.timer
fi fi
# Dev # Dev
@ -207,20 +208,17 @@ elif [ $ARCH == 1 ]; then
else else
inst make inst make
fi fi
inst git inst git
# Text editor # Text editor
if [ $TERMUX == 1 ]; then inst neovim
inst vim-python if [ $DEBIAN == 1]; then
elif [ $DEBIAN == 1 ]; then inst python-neovim pyhon3-neovim
inst vim-nox elif [ $ARCH == 1]; then
if [ $ADMIN == 0 ]; then inst python2-neovim python-neovim
debloc altern vim nox
fi
else
inst vim
fi fi
if [ $DEBIAN == 1 ]; then if [ $DEBIAN == 1 ]; then
inst exuberant-ctags inst exuberant-ctags
else else
@ -233,54 +231,13 @@ if [ $GUI == 1 ]; then
.Xresources.d/configure .Xresources.d/configure
# Desktop manager # Desktop manager
inst i3 i3lock dunst unclutter xautolock feh numlockx scrot rxvt-unicode xclip inst dunst feh i3-wm i3lock numlockx qutebrowser rofi rxvt-unicode scrot trayer unclutter xautolock xclip
curl "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/a8386aae19e200ddb0f6845b5feeee5eb7013687/fonts/fontawesome-webfont.ttf" > ~/.local/share/fonts/fontawesome-webfont.ttf
if [ $ARCH == 1 ]; then if [ $ARCH == 1 ]; then
inst xorg-xinit inst xorg-xinit xorg-backlight
altInst polybar-git autorandr-git keynav-enhanced pacmixer altInst lemonbar-xft-git autorandr-git keynav-enhanced pacmixer rofi-pass
else elif [ $DEBIAN == 1 ]; then
# Compiling polybar # TODO autorandr pacmixer rofi-pass
if ! which polybar > /dev/null; then inst lemonbar keynav xbacklight
inst debhelper cmake libxcb-icccm4-dev libxcb-image0-dev libxcb-randr0-dev libx11-dev libxcb1-dev libxcb-util-dev libx11-xcb-dev linux-libc-dev libboost-dev x11proto-core-dev libxcb-ewmh-dev libxft-dev libasound2-dev libiw-dev libmpdclient-dev xcb-proto python-xcbgen libxcb-xkb-dev i3-wm libcairo2-dev libxcb-xrm-dev
# TODO Figure which one are really needed
#inst libasound2 libc6 libgcc1 libiw30 libmpdclient2 libstdc++6 libx11-6 libx11-xcb1 libxcb-ewmh2 libxcb-icccm4 libxcb-randr0 libxcb-xkb1 libxcb1 libxft2
# ↓ really needed
inst libcairo2-dev libxcb-xkb-dev libxcb-randr0-dev xcb-proto libxcb-image0-dev libxcb-icccm4-dev libxcb-ewmh-dev libxcb-util0-dev python-xcbgen
TMP=$(mktemp -d)
git clone --branch 3.0.5 --recursive https://github.com/jaagr/polybar $TMP
mkdir $TMP/build
cd $TMP/build
cmake ..
make -j`nproc`
strip bin/polybar
mv bin/polybar ~/.local/bin/
rm -rf $TMP
fi
fi
if [ $DEBIAN == 1 ]; then
inst suckless-tools keynav
if [ $ADMIN == 0 ]; then
debloc altern dmenu xft
fi
else
inst dmenu
fi
if [ "$(source /etc/os-release; echo $NAME)" == "Manjaro Linux" ]; then
inst menda-themes menda-circle-icon-theme xcursor-menda
fi
# qutebrowser
if [ $DEBIAN == 1 ]; then
inst python3-lxml python-tox python3-pyqt5 python3-pyqt5.qtwebkit python3-pyqt5.qtquick python3-sip python3-jinja2 python3-pygments python3-yaml python3-pyqt5.qtsql libqt5sql5-sqlite python3-pyqt5.qtwebengine python3-pyqt5.qtopengl python3-opengl
TMP_DIR=$(mktemp -d)
$(cd $TMP_DIR; wget https://qutebrowser.org/python3-pypeg2_2.15.2-1_all.deb)
$(cd $TMP_DIR; wget https://github.com/qutebrowser/qutebrowser/releases/download/v0.11.0/qutebrowser_0.11.0-1_all.deb)
instFile $TMP_DIR/*.deb
rm -rf $TMP_DIR
elif [ $ARCH == 1 ]; then
inst qutebrowser qt5-webengine python-opengl
fi fi
# Screen filter # Screen filter
@ -294,48 +251,47 @@ if [ $GUI == 1 ]; then
rm $TMP rm $TMP
fi fi
fi fi
# Graphical vim
if [ $DEBIAN == 1 ]; then
inst vim-gtk
else
inst gvim
fi
fi fi
if [ $EXTRA == 1 ]; then if [ $EXTRA == 1 ]; then
# Extra dev # Extra dev (not on mobile though ^^)
inst cmake clang llvm npm if [ $TERMUX == 0 ]; then
inst python-rope inst cmake clang llvm ccache python-pip
fi
# Extra CLI # Extra CLI
inst ffmpeg youtube-dl optipng syncthing ccache mutt inst ffmpeg optipng syncthing mutt jq
systemdUserUnit syncthing.service systemdUserUnit syncthing
if [ $ARCH == 1 ]; then if [ $ARCH == 1 ]; then
inst jq insta pandoc youtube-dl translate-shell
altInst pdftk translate-shell git-lfs js-beautify insect visidata-git altInst insect pdftk visidata
# Orga # Orga
# TODO For others # TODO For others
inst vdirsyncer khard inst vdirsyncer khard todoman offlineimap khal
altInst khal todoman offlineimap
systemdUserUnit vdirsyncer.timer systemdUserUnit vdirsyncer.timer
elif [ $DEBIAN == 1]; then
inst pandoc pdftk visidata translate-shell youtube-dl
else else
# translate-shell # translate-shell
curl -L git.io/trans > ~/.local/bin/trans curl -L git.io/trans > ~/.local/bin/trans
chmod +x ~/.local/bin/trans chmod +x ~/.local/bin/trans
# TODO Others
fi fi
# Extra GUI # Extra GUI
if [ $GUI == 1 ]; then if [ $GUI == 1 ]; then
inst vlc gimp mpd thunar noto-fonts-emoji musescore inst vlc gimp mpd thunar musescore evince pdfpc texlive-{most,lang}
if [ $ARCH == 1 ]; then if [ $ARCH == 1 ]; then
inst simplescreenrecorder inst simplescreenrecorder mpc
altInst pacmixer xcursor-menda-git menda-themes-git menda-maia-icon-theme vimpc-git mpc ashuffle-git altInst vimpc-git ashuffle-git ttf-emojione-color
fi fi
# TODO Others
fi fi
fi fi

22
scripts/unziptree Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import subprocess
for root, dirs, files in os.walk("."):
for name in files:
base, ext = os.path.splitext(name)
if ext.lower() != ".zip":
continue
filepath = os.path.join(root, name)
dirpath = os.path.join(root, base)
print(filepath)
os.mkdir(dirpath)
cmd = ["unzip", os.path.realpath(filepath)]
r = subprocess.run(cmd, cwd=dirpath)
r.check_returncode()
os.unlink(filepath)

6
vimrc
View File

@ -58,6 +58,8 @@ Plug 'junegunn/fzf.vim'
Plug 'ervandew/supertab' Plug 'ervandew/supertab'
Plug 'dpelle/vim-LanguageTool' Plug 'dpelle/vim-LanguageTool'
Plug 'terryma/vim-smooth-scroll' Plug 'terryma/vim-smooth-scroll'
Plug 'vim-pandoc/vim-pandoc'
Plug 'vim-pandoc/vim-pandoc-syntax'
call plug#end() call plug#end()
@ -143,6 +145,10 @@ let g:SuperTabContextDefaultCompletionType = "<c-n>"
let g:languagetool_jar = "/usr/share/java/languagetool/languagetool-commandline.jar" let g:languagetool_jar = "/usr/share/java/languagetool/languagetool-commandline.jar"
""" vim-pandoc """
let g:pandoc#modules#disabled = ["folding"]
let g:pandoc#syntax#conceal#use = 0
""" VIM SETTINGS """ """ VIM SETTINGS """