inital commit of clone of old repo

This commit is contained in:
Contegix Support 2015-04-12 18:06:28 -05:00
parent 5529249e29
commit 77e1a1340d
3071 changed files with 157540 additions and 4 deletions

28
BEAST/README Normal file
View File

@ -0,0 +1,28 @@
BEAST - Reincarnated on a Dell Inspiron Mini Netbook, up from a old PowerEdge 2500.
--- Has since been replaced with BEAST2
Also runs Docker, so it can run different distros on the backend.
Screenfetch:
.. root@BEAST
.PLTJ. OS: CentOS
<><><><> Kernel: x86_64 Linux 3.2.40-grsec-dotcloud
KKSSV' 4KKK LJ KKKL.'VSSKK Uptime: 22d 12h 45m
KKV' 4KKKKK LJ KKKKAL 'VKK Packages: 479
V' ' 'VKKKK LJ KKKKV' ' 'V Shell: zsh 4.3.10
.4MA.' 'VKK LJ KKV' '.4Mb. CPU: Intel Atom CPU N455 @ 1.667GHz
. KKKKKA.' 'V LJ V' '.4KKKKK . RAM: 944MB / 1991MB
.4D KKKKKKKA.'' LJ ''.4KKKKKKK FA.
<QDD ++++++++++++ ++++++++++++ GFD>
'VD KKKKKKKK'.. LJ ..'KKKKKKKK FV
' VKKKKK'. .4 LJ K. .'KKKKKV '
'VK'. .4KK LJ KKA. .'KV'
A. . .4KKKK LJ KKKKA. . .4
KKA. 'KKKKK LJ KKKKK' .4KK
KKSSA. VKKK LJ KKKV .4SSKK
<><><><>
'MKKM'
''

1
BEAST/hwinfo Normal file
View File

@ -0,0 +1 @@
2 * Intel(R) Atom(TM) CPU N455 @ 1.66GHz, 1.9 GB mem, 2.0 GB swap

324
BEAST/lshw Normal file
View File

@ -0,0 +1,324 @@
beast
description: Portable Computer
product: Inspiron 1018
vendor: Dell Inc.
version: A00
width: 64 bits
capabilities: smbios-2.5 dmi-2.5 vsyscall32
configuration: administrator_password=disabled boot=oem-specific chassis=portable frontpanel_password=unknown keyboard_password=unknown power-on_password=disabled
*-core
description: Motherboard
physical id: 0
*-firmware
description: BIOS
vendor: Dell Inc.
physical id: 1
version: A00
date: 06/30/2010
size: 103KiB
capacity: 1984KiB
capabilities: pci pnp upgrade shadowing escd cdboot bootselect edd int5printscreen int9keyboard int14serial int17printer int10video acpi usb smartbattery biosbootspecification netboot
*-board UNCLAIMED
description: Motherboard
product: 0GHG2G
vendor: Dell Inc.
physical id: 3
version: A00
serial: . .CN129611530618.
slot: Not Applicable
*-cpu
description: CPU
product: Pentium 4
vendor: Intel Corp.
physical id: 5
bus info: cpu@0
version: C1
slot: CPU 1
size: 1667MHz
capacity: 1667MHz
width: 64 bits
clock: 667MHz
capabilities: x86-64 fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx constant_tsc arch_perfmon pebs bts rep_good nopl aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm movbe lahf_lm dtherm cpufreq
configuration: cores=1 enabledcores=1 threads=2
*-cache:0
description: L1 cache
physical id: 6
slot: L1 Cache
size: 16KiB
capacity: 16KiB
capabilities: asynchronous internal write-back
*-cache:1
description: L2 cache
physical id: 7
slot: L2 Cache
size: 512KiB
capacity: 512KiB
capabilities: burst internal write-back
*-memory
description: System Memory
physical id: 12
slot: System board or motherboard
size: 2GiB
*-bank
description: SODIMM Synchronous 667 MHz (1.5 ns)
product: CM3X2GSD1066
vendor: AMI
physical id: 0
serial: 00000000
slot: J6G1
size: 2GiB
width: 64 bits
clock: 667MHz (1.5ns)
*-pci
description: Host bridge
product: Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge
vendor: Intel Corporation
physical id: 100
bus info: pci@0000:00:00.0
version: 00
width: 32 bits
clock: 33MHz
configuration: driver=agpgart-intel
resources: irq:0
*-display:0
description: VGA compatible controller
product: Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
vendor: Intel Corporation
physical id: 2
bus info: pci@0000:00:02.0
version: 00
width: 32 bits
clock: 33MHz
capabilities: msi pm vga_controller bus_master cap_list rom
configuration: driver=i915 latency=0
resources: irq:43 memory:f0200000-f027ffff ioport:18d0(size=8) memory:d0000000-dfffffff memory:f0000000-f00fffff
*-display:1 UNCLAIMED
description: Display controller
product: Atom Processor D4xx/D5xx/N4xx/N5xx Integrated Graphics Controller
vendor: Intel Corporation
physical id: 2.1
bus info: pci@0000:00:02.1
version: 00
width: 32 bits
clock: 33MHz
capabilities: pm bus_master cap_list
configuration: latency=0
resources: memory:f0280000-f02fffff
*-multimedia
description: Audio device
product: N10/ICH 7 Family High Definition Audio Controller
vendor: Intel Corporation
physical id: 1b
bus info: pci@0000:00:1b.0
version: 02
width: 64 bits
clock: 33MHz
capabilities: pm msi pciexpress bus_master cap_list
configuration: driver=snd_hda_intel latency=0
resources: irq:44 memory:f0300000-f0303fff
*-pci:0
description: PCI bridge
product: N10/ICH 7 Family PCI Express Port 1
vendor: Intel Corporation
physical id: 1c
bus info: pci@0000:00:1c.0
version: 02
width: 32 bits
clock: 33MHz
capabilities: pci pciexpress msi pm normal_decode bus_master cap_list
configuration: driver=pcieport
resources: irq:40 ioport:2000(size=4096) memory:80200000-805fffff ioport:f0f00000(size=1048576)
*-network
description: Ethernet interface
product: RTL8101E/RTL8102E PCI Express Fast Ethernet controller
vendor: Realtek Semiconductor Co., Ltd.
physical id: 0
bus info: pci@0000:05:00.0
logical name: p1p1
version: 05
serial: 5c:26:0a:52:af:26
size: 100Mbit/s
capacity: 100Mbit/s
width: 64 bits
clock: 33MHz
capabilities: pm msi pciexpress msix vpd bus_master cap_list ethernet physical tp mii 10bt 10bt-fd 100bt 100bt-fd autonegotiation
configuration: autonegotiation=on broadcast=yes driver=r8169 driverversion=2.3LK-NAPI duplex=full firmware=N/A ip=192.168.1.3 latency=0 link=yes multicast=yes port=MII speed=100Mbit/s
resources: irq:45 ioport:2000(size=256) memory:f0f2c000-f0f2cfff memory:f0f18000-f0f1bfff
*-pci:1
description: PCI bridge
product: N10/ICH 7 Family PCI Express Port 2
vendor: Intel Corporation
physical id: 1c.1
bus info: pci@0000:00:1c.1
version: 02
width: 32 bits
clock: 33MHz
capabilities: pci pciexpress msi pm normal_decode bus_master cap_list
configuration: driver=pcieport
resources: irq:41 ioport:3000(size=4096) memory:f0100000-f01fffff ioport:80000000(size=2097152)
*-network UNCLAIMED
description: Network controller
product: RTL8188CE 802.11b/g/n WiFi Adapter
vendor: Realtek Semiconductor Co., Ltd.
physical id: 0
bus info: pci@0000:07:00.0
version: 01
width: 64 bits
clock: 33MHz
capabilities: pm msi pciexpress cap_list
configuration: latency=0
resources: ioport:3000(size=256) memory:f0100000-f0103fff
*-usb:0
description: USB controller
product: N10/ICH 7 Family USB UHCI Controller #1
vendor: Intel Corporation
physical id: 1d
bus info: pci@0000:00:1d.0
version: 02
width: 32 bits
clock: 33MHz
capabilities: uhci bus_master
configuration: driver=uhci_hcd latency=0
resources: irq:23 ioport:1820(size=32)
*-usb:1
description: USB controller
product: N10/ICH 7 Family USB UHCI Controller #2
vendor: Intel Corporation
physical id: 1d.1
bus info: pci@0000:00:1d.1
version: 02
width: 32 bits
clock: 33MHz
capabilities: uhci bus_master
configuration: driver=uhci_hcd latency=0
resources: irq:19 ioport:1840(size=32)
*-usb:2
description: USB controller
product: N10/ICH 7 Family USB UHCI Controller #3
vendor: Intel Corporation
physical id: 1d.2
bus info: pci@0000:00:1d.2
version: 02
width: 32 bits
clock: 33MHz
capabilities: uhci bus_master
configuration: driver=uhci_hcd latency=0
resources: irq:18 ioport:1860(size=32)
*-usb:3
description: USB controller
product: N10/ICH 7 Family USB UHCI Controller #4
vendor: Intel Corporation
physical id: 1d.3
bus info: pci@0000:00:1d.3
version: 02
width: 32 bits
clock: 33MHz
capabilities: uhci bus_master
configuration: driver=uhci_hcd latency=0
resources: irq:16 ioport:1880(size=32)
*-usb:4
description: USB controller
product: N10/ICH 7 Family USB2 EHCI Controller
vendor: Intel Corporation
physical id: 1d.7
bus info: pci@0000:00:1d.7
version: 02
width: 32 bits
clock: 33MHz
capabilities: pm debug ehci bus_master cap_list
configuration: driver=ehci_hcd latency=0
resources: irq:23 memory:f0504000-f05043ff
*-pci:2
description: PCI bridge
product: 82801 Mobile PCI Bridge
vendor: Intel Corporation
physical id: 1e
bus info: pci@0000:00:1e.0
version: e2
width: 32 bits
clock: 33MHz
capabilities: pci subtractive_decode bus_master cap_list
*-isa
description: ISA bridge
product: NM10 Family LPC Controller
vendor: Intel Corporation
physical id: 1f
bus info: pci@0000:00:1f.0
version: 02
width: 32 bits
clock: 33MHz
capabilities: isa bus_master cap_list
configuration: latency=0
*-storage
description: SATA controller
product: N10/ICH7 Family SATA Controller [AHCI mode]
vendor: Intel Corporation
physical id: 1f.2
bus info: pci@0000:00:1f.2
logical name: scsi0
version: 02
width: 32 bits
clock: 66MHz
capabilities: storage msi pm ahci_1.0 bus_master cap_list emulated
configuration: driver=ahci latency=0
resources: irq:42 ioport:18e8(size=8) ioport:18dc(size=4) ioport:18e0(size=8) ioport:18d8(size=4) ioport:18c0(size=16) memory:f0504400-f05047ff
*-disk
description: ATA Disk
product: WDC WD1600BEVS-7
vendor: Western Digital
physical id: 0.0.0
bus info: scsi@0:0.0.0
logical name: /dev/sda
version: 04.0
serial: WD-WXEY07K39367
size: 149GiB (160GB)
capabilities: partitioned partitioned:dos
configuration: ansiversion=5 logicalsectorsize=512 sectorsize=512 signature=0009ac27
*-volume:0
description: Linux filesystem partition
vendor: Linux
physical id: 1
bus info: scsi@0:0.0.0,1
logical name: /dev/sda1
logical name: /boot
version: 1.0
serial: 999d3b16-02f6-43f3-b79a-9e829a73393b
size: 200MiB
capacity: 200MiB
capabilities: primary extended_attributes ext2 initialized
configuration: filesystem=ext2 modified=2013-09-19 01:59:14 mount.fstype=ext2 mount.options=rw,relatime,errors=continue mounted=2013-06-16 11:28:21 state=mounted
*-volume:1
description: EXT4 volume
vendor: Linux
physical id: 2
bus info: scsi@0:0.0.0,2
logical name: /dev/sda2
logical name: /
version: 1.0
serial: b63650b2-ef91-4183-9a67-426708cde1b2
size: 148GiB
capacity: 148GiB
capabilities: primary journaled extended_attributes large_files huge_files dir_nlink recover extents ext4 ext2 initialized
configuration: created=2013-01-12 00:42:03 filesystem=ext4 lastmountpoint=/ modified=2013-05-16 21:23:46 mount.fstype=ext4 mount.options=rw,relatime,user_xattr,barrier=1,data=ordered mounted=2013-09-19 01:59:14 state=mounted
*-serial
description: SMBus
product: N10/ICH 7 Family SMBus Controller
vendor: Intel Corporation
physical id: 1f.3
bus info: pci@0000:00:1f.3
version: 02
width: 32 bits
clock: 33MHz
configuration: driver=i801_smbus latency=0
resources: irq:19 ioport:18a0(size=32)
*-battery
product: SmartBattery
vendor: SDI
physical id: 1
slot: System Battery Bay
capacity: 48840mWh
configuration: voltage=11.1V
*-remoteaccess UNCLAIMED
vendor: Intel
physical id: 2
capabilities: inbound

View File

@ -0,0 +1 @@
NOVA AwesomeWM configuration directory

@ -0,0 +1 @@
Subproject commit 771f42d9bd79df39b3a8806c6cb08dbfed8a961c

@ -0,0 +1 @@
Subproject commit f65180adc47792da57e5e658f30b855c4dfee29b

View File

@ -0,0 +1,291 @@
local menue0e4fc6213e8b3593495a7260c3a4c2e = {
{"Accerciser", "/usr/bin/accerciser", "/usr/share/icons/hicolor/16x16/apps/accerciser.png" },
{"Screen Reader", "orca --replace", "/usr/share/icons/hicolor/16x16/apps/orca.png" },
}
local menu98edb85b00d9527ad5acebe451b3fae6 = {
{"7-Zip FM", "7zFM", "/usr/share/icons/hicolor/32x32/apps/p7zip.png" },
{"Akonaditray", "akonaditray", "/usr/share/icons/hicolor/22x22/apps/akonaditray.png" },
{"Archive Manager", "file-roller ", "/usr/share/icons/hicolor/16x16/apps/file-roller.png" },
{"AutoKey", "autokey-gtk -c"},
{"AutoKey (KDE)", "autokey-qt"},
{"Calculator", "gnome-calculator", "/usr/share/icons/gnome/16x16/apps/accessories-calculator.png" },
{"Character Map", "gucharmap", "/usr/share/icons/gnome/16x16/apps/accessories-character-map.png" },
{"Clocks", "gnome-clocks", "/usr/share/icons/hicolor/16x16/apps/gnome-clocks.png" },
{"Desktop Search", "tracker-needle", "/usr/share/icons/gnome/16x16/actions/system-search.png" },
{"Disks", "gnome-disks", "/usr/share/icons/hicolor/16x16/apps/gnome-disks.png" },
{"Engrampa Archive Manager", "engrampa ", "/usr/share/icons/hicolor/16x16/apps/engrampa.png" },
{"Fern Wifi Cracker", "gksu fern-wifi-cracker", "///usr/share/fern-wifi-cracker/resources/screen_splash.png" },
{"Files", "nautilus --new-window ", "/usr/share/icons/gnome/16x16/apps/system-file-manager.png" },
{"Font Viewer", "gnome-font-viewer ", "/usr/share/icons/gnome/16x16/apps/preferences-desktop-font.png" },
{"Galculator", "galculator", "/usr/share/icons/hicolor/48x48/apps/galculator.png" },
{"Growl For Linux", "/usr/bin/gol", "///usr/share/growl-for-linux/data/icon.png" },
{"HP Device Manager", "hp-toolbox", "///usr/share/hplip/data/images/128x128/hp_logo.png" },
{"Hwacha", "/opt/hwacha/hwacha", "///opt/hwacha/hwacha.png" },
{"IPython Qt console", "ipython qtconsole", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"IPython2 Qt console", "ipython2 qtconsole", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"Johnny", "johnny", "/usr/share/pixmaps/johnny.png" },
{"KeePassX", "keepassx ", "/usr/share/pixmaps/keepassx.xpm" },
{"Maps", "gnome-maps", "/usr/share/icons/hicolor/16x16/apps/gnome-maps.png" },
{"Metasploit AV Evasion", "xterm -e metasploitavevasion", "/usr/share/pixmaps/metasploitavevasion.png" },
{"Nemo", "nemo ", "/usr/share/icons/gnome/16x16/places/folder.png" },
{"Notes", "bijiben ", "/usr/share/icons/hicolor/16x16/apps/bijiben.png" },
{"PCManFM File Manager", "pcmanfm-qt ", "/usr/share/icons/gnome/16x16/apps/system-file-manager.png" },
{"PacmanXG", "ssx pacmanxg"},
{"Passwords and Keys", "/usr/bin/seahorse", "/usr/share/icons/hicolor/16x16/apps/seahorse.png" },
{"Pip3line", "pip3line", "///usr/share/icons/hicolor/128x128/apps/pip3line.png" },
{"Pluma", "pluma ", "/usr/share/icons/gnome/16x16/apps/accessories-text-editor.png" },
{"RFDump", "rfdump", "///usr/share/pixmaps/rfdump.png" },
{"Random Wordlist Generator", "randomwordlistgenerator", "///usr/share/pixmaps/randomwordlistgenerator.png" },
{"Recoll", "recoll", "/usr/share/icons/hicolor/48x48/apps/recoll.png" },
{"Root Terminal", "gksu -l gnome-terminal", "/usr/share/pixmaps/gksu-root-terminal.png" },
{"Screen Reader", "orca --replace", "/usr/share/icons/hicolor/16x16/apps/orca.png" },
{"Screenshot", "gnome-screenshot --interactive", "/usr/share/icons/gnome/16x16/apps/applets-screenshooter.png" },
{"Synapse", "synapse"},
{"Tomboy Notes", "tomboy --search", "/usr/share/icons/hicolor/16x16/apps/tomboy.png" },
{"Weather", "gapplication launch org.gnome.Weather.Application", "/usr/share/icons/hicolor/16x16/apps/org.gnome.Weather.Application.png" },
{"Zim Desktop Wiki", "zim", "/usr/share/pixmaps/zim.png" },
{"gedit", "gedit ", "/usr/share/icons/gnome/16x16/apps/accessories-text-editor.png" },
{"ipython", "xterm -e ipython", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"ipython2", "xterm -e ipython2", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"ownCloud desktop sync client ", "owncloud", "/usr/share/icons/hicolor/22x22/apps/owncloud.png" },
{"x3270", "/usr/bin/x3270", "///usr/share/pixmaps/x3270-icon2.xpm" },
}
local menude7a22a0c94aa64ba2449e520aa20c99 = {
{"LibreOffice Math", "libreoffice --math ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-math.png" },
}
local menu251bd8143891238ecedc306508e29017 = {
{"AisleRiot Solitaire", "sol", "/usr/share/icons/hicolor/16x16/apps/gnome-aisleriot.png" },
{"Chess", "gnome-chess", "/usr/share/icons/hicolor/16x16/apps/gnome-chess.png" },
{"Dosbox", "dosbox", "/usr/share/pixmaps/dosbox.png" },
{"Dwarf Fortress", "dwarffortress", "///usr/share/pixmaps/dwarffortress.png" },
{"Feed The Beast", "feedthebeast", "/usr/share/icons/hicolor/16x16/apps/feedthebeast.png" },
{"Firestorm Second Life viewer", "/usr/bin/firestorm", "///usr/share/pixmaps/firestorm_icon.png" },
{"Five or More", "five-or-more", "/usr/share/icons/hicolor/16x16/apps/five-or-more.png" },
{"Four-in-a-row", "four-in-a-row", "/usr/share/icons/hicolor/16x16/apps/four-in-a-row.png" },
{"Game Conqueror", "gameconqueror", "/usr/share/pixmaps/GameConqueror_128x128.png" },
{"Hitori", "hitori", "/usr/share/icons/hicolor/16x16/apps/hitori.png" },
{"Iagno", "iagno", "/usr/share/icons/hicolor/16x16/apps/iagno.png" },
{"Klotski", "gnome-klotski", "/usr/share/icons/hicolor/16x16/apps/gnome-klotski.png" },
{"Lights Off", "lightsoff"},
{"Mahjongg", "gnome-mahjongg", "/usr/share/icons/hicolor/16x16/apps/gnome-mahjongg.png" },
{"Mari0", "mari0"},
{"Minecraft", "minecraft", "///usr/share/pixmaps/minecraft.png" },
{"Mines", "gnome-mines", "/usr/share/icons/hicolor/16x16/apps/gnome-mines.png" },
{"Minetest", "minetest"},
{"Nibbles", "gnome-nibbles", "/usr/share/icons/hicolor/16x16/apps/gnome-nibbles.png" },
{"PlayOnLinux", "playonlinux", "///usr/share/playonlinux/etc/playonlinux.png" },
{"Quadrapassel", "quadrapassel", "/usr/share/icons/hicolor/16x16/apps/quadrapassel.png" },
{"Robots", "gnome-robots", "/usr/share/icons/hicolor/16x16/apps/gnome-robots.png" },
{"RuneScape", "/opt/runescape/runescape", "///opt/runescape/share/img/runescape.png" },
{"RuneScape OldSchool", "/opt/runescape/runescape --prmfile=oldschool.prm", "///opt/runescape/share/img/runescape.png" },
{"Runescape Client Updater", "xterm -e /opt/runescape/rsu/rsu-query rsu.download.client", "///opt/runescape/share/img/update-runescape.png" },
{"StarMade", "starmade", "///usr/share/pixmaps/starmade.png" },
{"Steam", "env STEAM_FRAME_FORCE_CLOSE=1 /usr/bin/steam ", "/usr/share/icons/hicolor/16x16/apps/steam.png" },
{"Steam", "/usr/bin/steam ", "/usr/share/icons/hicolor/16x16/apps/steam.png" },
{"Sudoku", "gnome-sudoku", "/usr/share/icons/hicolor/16x16/apps/gnome-sudoku.png" },
{"SuperTux 2", "supertux2", "/usr/share/pixmaps/supertux.png" },
{"Swell Foop", "swell-foop", "/usr/share/icons/hicolor/16x16/apps/swell-foop.png" },
{"Tali", "tali", "/usr/share/icons/hicolor/16x16/apps/tali.png" },
{"Tetravex", "gnome-tetravex", "/usr/share/icons/hicolor/16x16/apps/gnome-tetravex.png" },
{"Xonotic (GLX)", "/usr/bin/xonotic-glx", "/usr/share/icons/hicolor/16x16/apps/xonotic.png" },
{"Xonotic (SDL)", "/usr/bin/xonotic-sdl", "/usr/share/icons/hicolor/16x16/apps/xonotic.png" },
}
local menud334dfcea59127bedfcdbe0a3ee7f494 = {
{"Document Viewer", "evince ", "/usr/share/icons/hicolor/16x16/apps/evince.png" },
{"Eye of MATE Image Viewer", "eom ", "/usr/share/icons/hicolor/16x16/apps/eom.png" },
{"FontForge", "fontforge ", "/usr/share/icons/hicolor/16x16/apps/fontforge.png" },
{"GNU Image Manipulation Program", "gimp-2.8 ", "/usr/share/icons/hicolor/16x16/apps/gimp.png" },
{"Image Viewer", "eog ", "/usr/share/icons/hicolor/16x16/apps/eog.png" },
{"Image Viewer", "gpicview ", "/usr/share/icons/hicolor/48x48/apps/gpicview.png" },
{"LibreOffice Draw", "libreoffice --draw ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-draw.png" },
{"ScreenCloud", "/opt/screencloud/screencloud.sh", "/usr/share/icons/hicolor/16x16/apps/screencloud.png" },
{"xgps", "xgps", "///usr/share/gpsd/gpsd-logo.png" },
{"xgpsspeed", "xgpsspeed", "///usr/share/gpsd/gpsd-logo.png" },
}
local menuc8205c7636e728d448c2774e6a4a944b = {
{"Amap", "amap"},
{"Angry IP Scanner", "ipscan"},
{"Avahi SSH Server Browser", "/usr/bin/bssh", "/usr/share/icons/gnome/16x16/devices/network-wired.png" },
{"Avahi VNC Server Browser", "/usr/bin/bvnc", "/usr/share/icons/gnome/16x16/devices/network-wired.png" },
{"Browse Mirrored Websites", "webhttrack browse", "/usr/share/icons/hicolor/16x16/apps/httrack.png" },
{"Caja Dropbox", "caja-dropbox start -i", "/usr/share/icons/hicolor/16x16/apps/caja-dropbox.png" },
{"Chromium", "chromium ", "/usr/share/icons/hicolor/16x16/apps/chromium.png" },
{"Dell SonicWALL NetExtender", "/usr/bin/netExtenderGui", "///usr/share/netExtender/icons/nx48.xpm" },
{"ELinks", "xterm -e /usr/bin/elinks ", "/usr/share/icons/gnome/16x16/mimetypes/html.png" },
{"Empathy", "empathy", "/usr/share/icons/hicolor/16x16/apps/empathy.png" },
{"EtherApe", "etherape", "/usr/share/pixmaps/etherape.png" },
{"Ettercap", "ettercap-pkexec -G", "/usr/share/pixmaps/ettercap.svg" },
{"FileZilla", "filezilla", "/usr/share/icons/hicolor/16x16/apps/filezilla.png" },
{"Firefox", "/usr/lib/firefox/firefox ", "/usr/share/icons/hicolor/16x16/apps/firefox.png" },
{"Firefox Developer", "firefox-developer ", "/usr/share/pixmaps/firefox-developer-icon.png" },
{"Gajim", "gajim", "/usr/share/icons/hicolor/64x64/apps/gajim.png" },
{"Gnome-RDP", "gnome-rdp", "/usr/share/pixmaps/gnome-rdp.png" },
{"Google Chrome", "/usr/bin/google-chrome-stable ", "/usr/share/icons/hicolor/16x16/apps/google-chrome.png" },
{"Google Chrome (beta)", "/usr/bin/google-chrome-beta ", "/usr/share/icons/hicolor/16x16/apps/google-chrome-beta.png" },
{"Google Chrome (unstable)", "/usr/bin/google-chrome-unstable ", "/usr/share/icons/hicolor/16x16/apps/google-chrome-unstable.png" },
{"KRDC", "krdc -caption KRDC "},
{"KompoZer", "kompozer", "/usr/share/pixmaps/kompozer.png" },
{"Midori", "midori ", "/usr/share/icons/hicolor/16x16/apps/midori.png" },
{"Midori Private Browsing", "midori --private ", "/usr/share/icons/hicolor/16x16/apps/midori.png" },
{"Mumble", "mumble"},
{"Net Activity Viewer", "netactview", "/usr/share/pixmaps/netactview.png" },
{"Netsurf", "netsurf ", "/usr/share/pixmaps/netsurf.png" },
{"OnionShare", "/usr/bin/onionshare-gui", "///usr/share/pixmaps/onionshare80.xpm" },
{"Opera", "opera ", "/usr/share/icons/hicolor/16x16/apps/opera.png" },
{"Pidgin Internet Messenger", "pidgin", "/usr/share/icons/hicolor/16x16/apps/pidgin.png" },
{"Polari", "polari", "/usr/share/icons/hicolor/16x16/apps/polari.png" },
{"Quassel IRC (Client only)", "quasselclient", "/usr/share/icons/hicolor/16x16/apps/quassel.png" },
{"Remmina", "remmina", "/usr/share/icons/hicolor/16x16/apps/remmina.png" },
{"Remote Desktop Viewer", "vinagre ", "/usr/share/icons/gnome/16x16/apps/preferences-desktop-remote-desktop.png" },
{"SeaMonkey internet suite", "seamonkey", "/usr/share/pixmaps/seamonkey.png" },
{"Skype", "skype ", "/usr/share/icons/hicolor/16x16/apps/skype.png" },
{"Steam", "env STEAM_FRAME_FORCE_CLOSE=1 /usr/bin/steam ", "/usr/share/icons/hicolor/16x16/apps/steam.png" },
{"Steam", "/usr/bin/steam ", "/usr/share/icons/hicolor/16x16/apps/steam.png" },
{"TeamSpeak 3", "teamspeak3", "/usr/share/pixmaps/teamspeak3.xpm" },
{"TeamViewer 10", "/opt/teamviewer/tv_bin/script/teamviewer", "///opt/teamviewer/tv_bin/desktop/teamviewer.png" },
{"Thunderbird", "thunderbird ", "/usr/share/icons/hicolor/16x16/apps/thunderbird.png" },
{"TigerVNC Viewer", "/usr/bin/vncviewer", "/usr/share/icons/hicolor/16x16/apps/tigervnc.png" },
{"Transmission", "transmission-gtk ", "/usr/share/icons/hicolor/16x16/apps/transmission.png" },
{"UltraVNC Viewer", "ultravnc-viewer", "/usr/share/icons/hicolor/16x16/apps/ultravnc-viewer.png" },
{"UltraVNC Viewer Listen mode", "ultravnc-viewer -listen", "/usr/share/icons/hicolor/16x16/apps/ultravnc-viewer.png" },
{"Vivaldi", "/usr/bin/vivaldi-preview ", "/usr/share/icons/hicolor/16x16/apps/vivaldi.png" },
{"Wavemon", "wavemon"},
{"Web", "epiphany ", "/usr/share/icons/gnome/16x16/apps/web-browser.png" },
{"WebHTTrack Website Copier", "webhttrack", "/usr/share/icons/hicolor/16x16/apps/httrack.png" },
{"XSSer", "/usr/bin/xsser --gtk --silent", "///usr/share/xsser/gtk/images/xssericon_24x24.png" },
{"Zenmap", "zenmap ", "///usr/share/zenmap/pixmaps/zenmap.png" },
{"Zenmap (as root)", "/usr/share/zenmap/su-to-zenmap.sh ", "///usr/share/zenmap/pixmaps/zenmap.png" },
{"tcpjunk", "tcpjunk -x", "/usr/share/pixmaps/tcpjunk.png" },
{"w3af", "w3af-gui", "///usr/share/w3af/doc/sphinx/images/w3af-logo.png" },
}
local menudf814135652a5a308fea15bff37ea284 = {
{"Atril Document Viewer", "atril ", "/usr/share/icons/hicolor/16x16/apps/atril.png" },
{"Dictionary", "gnome-dictionary", "/usr/share/icons/gnome/16x16/apps/accessories-dictionary.png" },
{"Document Viewer", "evince ", "/usr/share/icons/hicolor/16x16/apps/evince.png" },
{"Evolution", "evolution ", "/usr/share/icons/hicolor/16x16/apps/evolution.png" },
{"LibreOffice", "libreoffice ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-startcenter.png" },
{"LibreOffice Base", "libreoffice --base ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-base.png" },
{"LibreOffice Calc", "libreoffice --calc ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-calc.png" },
{"LibreOffice Draw", "libreoffice --draw ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-draw.png" },
{"LibreOffice Impress", "libreoffice --impress ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-impress.png" },
{"LibreOffice Installer", "libreoffice-installer", "/usr/share/icons/gnome/16x16/actions/document-save.png" },
{"LibreOffice Math", "libreoffice --math ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-math.png" },
{"LibreOffice Writer", "libreoffice --writer ", "/usr/share/icons/hicolor/16x16/apps/libreoffice-writer.png" },
{"Wordview Microsoft doc Viewer", "wordview", "/usr/share/pixmaps/wordview.xpm" },
}
local menu6311ae17c1ee52b36e68aaf4ad066387 = {
{"Admsnmp", "admsnmp"},
{"Argtable", "argtable"},
{"Cuckoo", "/usr/bin/cuckoo"},
{"ut2004", "/usr/local/games/ut2004//ut2004", "///usr/local/games/ut2004//ut2004.xpm" },
}
local menue6f43c40ab1c07cd29e4e83e4ef6bf85 = {
{"Accerciser", "/usr/bin/accerciser", "/usr/share/icons/hicolor/16x16/apps/accerciser.png" },
{"Anjuta", "anjuta ", "/usr/share/icons/hicolor/16x16/apps/anjuta.png" },
{"Arduino", "arduino ", "/usr/share/icons/hicolor/256x256/apps/arduino.png" },
{"Brackets", "/opt/brackets/brackets ", "/usr/share/icons/hicolor/32x32/apps/brackets.png" },
{"CMake", "cmake-gui ", "/usr/share/icons/hicolor/32x32/apps/CMakeSetup.png" },
{"Database browser", "gda-browser-5.0", "/usr/share/pixmaps/gda-browser-5.0.png" },
{"Devhelp", "devhelp", "/usr/share/icons/hicolor/16x16/apps/devhelp.png" },
{"Dissy", "dissy", "/usr/share/pixmaps/dissy.svg" },
{"Emacs", "emacs ", "/usr/share/icons/hicolor/16x16/apps/emacs.png" },
{"Evan's Debugger", "edb"},
{"FLUID", "fluid ", "/usr/share/icons/hicolor/16x16/apps/fluid.png" },
{"Glade", "glade ", "/usr/share/icons/hicolor/16x16/apps/glade.png" },
{"IPython Qt console", "ipython qtconsole", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"IPython2 Qt console", "ipython2 qtconsole", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"KompoZer", "kompozer", "/usr/share/pixmaps/kompozer.png" },
{"NetBeans", "netbeans", "/usr/share/pixmaps/netbeans.png" },
{"OpenJDK Monitoring & Management Console", "/usr/bin/jconsole", "/usr/share/icons/hicolor/16x16/apps/java.png" },
{"OpenJDK Policy Tool", "/usr/bin/policytool", "/usr/share/icons/hicolor/16x16/apps/java.png" },
{"Qt Assistant", "/usr/lib/qt/bin/assistant", "/usr/share/icons/hicolor/32x32/apps/assistant.png" },
{"Qt Designer", "/usr/lib/qt/bin/designer", "/usr/share/icons/hicolor/128x128/apps/QtProject-designer.png" },
{"Qt Linguist", "/usr/lib/qt/bin/linguist", "/usr/share/icons/hicolor/16x16/apps/linguist.png" },
{"Qt QDbusViewer ", "/usr/lib/qt/bin/qdbusviewer", "/usr/share/icons/hicolor/32x32/apps/qdbusviewer.png" },
{"Qt4 Assistant ", "assistant-qt4", "/usr/share/icons/hicolor/32x32/apps/assistant-qt4.png" },
{"Qt4 Designer", "designer-qt4", "/usr/share/icons/hicolor/128x128/apps/designer-qt4.png" },
{"Qt4 Linguist ", "linguist-qt4", "/usr/share/icons/hicolor/16x16/apps/linguist-qt4.png" },
{"Qt4 QDbusViewer ", "qdbusviewer-qt4", "/usr/share/icons/hicolor/32x32/apps/qdbusviewer-qt4.png" },
{"Sublime Text 3 Dev", "subl3 ", "/usr/share/icons/hicolor/16x16/apps/sublime-text.png" },
{"VisualVM", "/usr/bin/visualvm", "///usr/share/visualvm/icon.png" },
{"gitg", "gitg --no-wd ", "/usr/share/icons/hicolor/16x16/apps/gitg.png" },
{"haroopad", "haroopad ", "/usr/share/icons/hicolor/16x16/apps/haroopad.png" },
{"ipython", "xterm -e ipython", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
{"ipython2", "xterm -e ipython2", "/usr/share/icons/gnome/16x16/status/gnome-netstatus-idle.png" },
}
local menu52dd1c847264a75f400961bfb4d1c849 = {
{"Audacious", "audacious ", "/usr/share/icons/hicolor/48x48/apps/audacious.png" },
{"Brasero", "brasero ", "/usr/share/icons/hicolor/16x16/apps/brasero.png" },
{"Cheese", "cheese", "/usr/share/icons/hicolor/16x16/apps/cheese.png" },
{"Clementine", "clementine ", "/usr/share/icons/hicolor/64x64/apps/application-x-clementine.png" },
{"Feed The Beast", "feedthebeast", "/usr/share/icons/hicolor/16x16/apps/feedthebeast.png" },
{"LXMusic simple music player", "lxmusic", "/usr/share/pixmaps/lxmusic.png" },
{"Music", "gnome-music", "/usr/share/icons/hicolor/16x16/apps/gnome-music.png" },
{"Pithos", "pithos"},
{"Plex Home Theater", "/usr/bin/plexhometheater.sh", "///usr/share/pixmaps/plexhometheater.png" },
{"PulseAudio Volume Control", "pavucontrol", "/usr/share/icons/hicolor/16x16/apps/multimedia-volume-control.png" },
{"Qt V4L2 test Utility", "qv4l2", "/usr/share/icons/hicolor/16x16/apps/qv4l2.png" },
{"Spotify", "spotify ", "/usr/share/icons/hicolor/16x16/apps/spotify-client.png" },
{"VLC media player", "/usr/bin/vlc --started-from-file ", "/usr/share/icons/hicolor/16x16/apps/vlc.png" },
{"Videos", "totem ", "/usr/share/icons/hicolor/16x16/apps/totem.png" },
{"Xnoise", "xnoise ", "/usr/share/icons/hicolor/32x32/apps/xnoise.png" },
}
local menuee69799670a33f75d45c57d1d1cd0ab3 = {
{"Avahi Zeroconf Browser", "/usr/bin/avahi-discover", "/usr/share/icons/gnome/16x16/devices/network-wired.png" },
{"Disk Usage Analyzer", "baobab", "/usr/share/icons/hicolor/16x16/apps/baobab.png" },
{"Disk Utility", "mate-disk"},
{"EtherApe", "etherape", "/usr/share/pixmaps/etherape.png" },
{"File Manager PCManFM", "pcmanfm ", "/usr/share/icons/gnome/16x16/apps/system-file-manager.png" },
{"GParted", "/usr/bin/gparted_polkit ", "/usr/share/icons/hicolor/16x16/apps/gparted.png" },
{"Guake Terminal", "guake", "/usr/share/icons/hicolor/16x16/apps/guake.png" },
{"Guymager", "gksudo guymager", "/usr/share/pixmaps/guymager_128.png" },
{"Htop", "xterm -e htop", "/usr/share/pixmaps/htop.png" },
{"LSHW", "/usr/sbin/gtk-lshw", "///usr/share/lshw/artwork/logo.svg" },
{"LXTerminal", "lxterminal", "/usr/share/icons/hicolor/128x128/apps/lxterminal.png" },
{"Logs", "gnome-logs", "/usr/share/icons/hicolor/16x16/apps/gnome-logs.png" },
{"MATE Terminal", "mate-terminal", "/usr/share/icons/gnome/16x16/apps/utilities-terminal.png" },
{"MDB Tools", "gmdb2", "///usr/share/gmdb/glade/logo.xpm" },
{"Manage Printing", "/usr/bin/xdg-open http://localhost:631/", "/usr/share/icons/hicolor/16x16/apps/cups.png" },
{"Network Tools", "mate-nettool", "/usr/share/icons/hicolor/16x16/apps/mate-nettool.png" },
{"Network Tools", "gnome-nettool", "/usr/share/icons/hicolor/16x16/apps/gnome-nettool.png" },
{"Ophcrack", "ophcrack", "///usr/share/ophcrack/pixmaps/os.xpm" },
{"Oracle VM VirtualBox", "VirtualBox ", "/usr/share/icons/hicolor/16x16/mimetypes/virtualbox.png" },
{"System Log", "gnome-system-log", "/usr/share/icons/hicolor/16x16/apps/logview.png" },
{"System Monitor", "gnome-system-monitor", "/usr/share/icons/gnome/16x16/apps/utilities-system-monitor.png" },
{"Task Manager", "lxtask", "/usr/share/icons/gnome/16x16/apps/utilities-system-monitor.png" },
{"Terminal", "gnome-terminal", "/usr/share/icons/gnome/16x16/apps/utilities-terminal.png" },
{"Terminator", "terminator", "/usr/share/icons/hicolor/16x16/apps/terminator.png" },
{"Tilda", "/usr/bin/tilda", "/usr/share/pixmaps/tilda.png" },
{"UXTerm", "uxterm", "/usr/share/pixmaps/xterm-color_48x48.xpm" },
{"Virtual Machine Manager", "virt-manager", "/usr/share/icons/hicolor/16x16/apps/virt-manager.png" },
{"Wireshark", "wireshark ", "/usr/share/icons/hicolor/16x16/apps/wireshark.png" },
{"XSSer", "/usr/bin/xsser --gtk --silent", "///usr/share/xsser/gtk/images/xssericon_24x24.png" },
{"XTerm", "xterm", "/usr/share/pixmaps/xterm-color_48x48.xpm" },
{"Xfce Terminal", "xfce4-terminal", "/usr/share/icons/gnome/16x16/apps/utilities-terminal.png" },
{"dconf Editor", "dconf-editor", "/usr/share/icons/hicolor/16x16/apps/dconf-editor.png" },
}
xdgmenu = {
{"Accessibility", menue0e4fc6213e8b3593495a7260c3a4c2e},
{"Accessories", menu98edb85b00d9527ad5acebe451b3fae6},
{"Education", menude7a22a0c94aa64ba2449e520aa20c99},
{"Games", menu251bd8143891238ecedc306508e29017},
{"Graphics", menud334dfcea59127bedfcdbe0a3ee7f494},
{"Internet", menuc8205c7636e728d448c2774e6a4a944b},
{"Office", menudf814135652a5a308fea15bff37ea284},
{"Other", menu6311ae17c1ee52b36e68aaf4ad066387},
{"Programming", menue6f43c40ab1c07cd29e4e83e4ef6bf85},
{"Sound & Video", menu52dd1c847264a75f400961bfb4d1c849},
{"System Tools", menuee69799670a33f75d45c57d1d1cd0ab3},
}

View File

@ -0,0 +1,46 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local client = client
local screen = screen
local aclient = require("awful.client")
local atag = require("awful.tag")
--- When loaded, this module makes sure that there's always a client that will have focus
-- on event such as tag switching, client unmanaging, etc.
module("awful.autofocus")
-- Give focus when clients appear/disappear.
-- @param obj An object that should have a .screen property.
local function check_focus(obj)
-- When no visible client has the focus...
if not client.focus or not client.focus:isvisible() then
local c = aclient.focus.history.get(obj.screen, 0)
if c then client.focus = c end
end
end
-- Give focus on tag selection change.
-- @param obj An object that should have a .screen property.
local function check_focus_screen(obj)
check_focus(obj)
if client.focus and client.focus.screen ~= obj.screen then
local c = nil
c = aclient.focus.history.get(obj.screen, 0)
if c then client.focus = c end
end
end
atag.attached_add_signal(nil, "property::selected", check_focus_screen)
client.add_signal("unmanage", check_focus)
client.add_signal("new", function(c)
c:add_signal("tagged", check_focus)
c:add_signal("untagged", check_focus)
c:add_signal("property::hidden", check_focus)
c:add_signal("property::minimized", check_focus)
end)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,52 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local setmetatable = setmetatable
local ipairs = ipairs
local capi = { button = button }
local util = require("awful.util")
--- Create easily new buttons objects ignoring certain modifiers.
module("awful.button")
--- Modifiers to ignore.
-- By default this is initialized as { "Lock", "Mod2" }
-- so the Caps Lock or Num Lock modifier are not taking into account by awesome
-- when pressing keys.
-- @name ignore_modifiers
-- @class table
ignore_modifiers = { "Lock", "Mod2" }
--- Create a new button to use as binding.
-- This function is useful to create several buttons from one, because it will use
-- the ignore_modifier variable to create more button with or without the ignored
-- modifiers activated.
-- For example if you want to ignore CapsLock in your buttonbinding (which is
-- ignored by default by this function), creating button binding with this function
-- will return 2 button objects: one with CapsLock on, and the other one with
-- CapsLock off.
-- @see button
-- @return A table with one or several button objects.
function new(mod, button, press, release)
local ret = {}
local subsets = util.subsets(ignore_modifiers)
for _, set in ipairs(subsets) do
ret[#ret + 1] = capi.button({ modifiers = util.table.join(mod, set),
button = button })
if press then
ret[#ret]:add_signal("press", function(bobj, ...) press(...) end)
end
if release then
ret[#ret]:add_signal("release", function (bobj, ...) release(...) end)
end
end
return ret
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,876 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local util = require("awful.util")
local tag = require("awful.tag")
local pairs = pairs
local type = type
local ipairs = ipairs
local table = table
local math = math
local setmetatable = setmetatable
local capi =
{
client = client,
mouse = mouse,
screen = screen,
}
--- Useful client manipulation functions.
module("awful.client")
-- Private data
data = {}
data.focus = {}
data.urgent = {}
data.marked = {}
data.properties = setmetatable({}, { __mode = 'k' })
-- Functions
urgent = {}
focus = {}
focus.history = {}
swap = {}
floating = {}
dockable = {}
property = {}
--- Get the first client that got the urgent hint.
-- @return The first urgent client.
function urgent.get()
if #data.urgent > 0 then
return data.urgent[1]
else
-- fallback behaviour: iterate through clients and get the first urgent
local clients = capi.client.get()
for k, cl in pairs(clients) do
if cl.urgent then
return cl
end
end
end
end
--- Jump to the client that received the urgent hint first.
-- @param merge If true then merge tags when clients are not visible.
function urgent.jumpto(merge)
local c = urgent.get()
if c then
local s = capi.client.focus and capi.client.focus.screen or capi.mouse.screen
-- focus the screen
if s ~= c.screen then
capi.mouse.screen = c.screen
end
-- Try to make client visible, this also covers e.g. sticky
local t = c:tags()[1]
if t and not c:isvisible() then
if merge then
t.selected = true
else
tag.viewonly(t)
end
end
-- focus the client
capi.client.focus = c
c:raise()
end
end
--- Adds client to urgent stack.
-- @param c The client object.
-- @param prop The property which is updated.
function urgent.add(c, prop)
if type(c) == "client" and prop == "urgent" and c.urgent then
table.insert(data.urgent, c)
end
end
--- Remove client from urgent stack.
-- @param c The client object.
function urgent.delete(c)
for k, cl in ipairs(data.urgent) do
if c == cl then
table.remove(data.urgent, k)
break
end
end
end
--- Remove a client from the focus history
-- @param c The client that must be removed.
function focus.history.delete(c)
for k, v in ipairs(data.focus) do
if v == c then
table.remove(data.focus, k)
break
end
end
end
--- Filter out window that we do not want handled by focus.
-- This usually means that desktop, dock and splash windows are
-- not registered and cannot get focus.
-- @param c A client.
-- @return The same client if it's ok, nil otherwise.
function focus.filter(c)
if c.type == "desktop"
or c.type == "dock"
or c.type == "splash"
or not c.focusable then
return nil
end
return c
end
--- Update client focus history.
-- @param c The client that has been focused.
function focus.history.add(c)
if focus.filter(c) then
-- Remove the client if its in stack
focus.history.delete(c)
-- Record the client has latest focused
table.insert(data.focus, 1, c)
end
end
--- Get the latest focused client for a screen in history.
-- @param screen The screen number to look for.
-- @param idx The index: 0 will return first candidate,
-- 1 will return second, etc.
-- @return A client.
function focus.history.get(screen, idx)
-- When this counter is equal to idx, we return the client
local counter = 0
local vc = visible(screen)
for k, c in ipairs(data.focus) do
if c.screen == screen then
for j, vcc in ipairs(vc) do
if vcc == c then
if counter == idx then
return c
end
-- We found one, increment the counter only.
counter = counter + 1
break
end
end
end
end
-- Argh nobody found in history, give the first one visible if there is one
-- that passes the filter.
if counter == 0 then
for k, v in ipairs(vc) do
if focus.filter(v) then
return v
end
end
end
end
--- Focus the previous client in history.
function focus.history.previous()
local sel = capi.client.focus
local s
if sel then
s = sel.screen
else
s = capi.mouse.screen
end
local c = focus.history.get(s, 1)
if c then capi.client.focus = c end
end
--- Get visible clients from a screen.
-- @param screen The screen number, or nil for all screens.
-- @return A table with all visible clients.
function visible(screen)
local cls = capi.client.get(screen)
local vcls = {}
for k, c in pairs(cls) do
if c:isvisible() then
table.insert(vcls, c)
end
end
return vcls
end
--- Get visible and tiled clients
-- @param screen The screen number, or nil for all screens.
-- @return A tabl with all visible and tiled clients.
function tiled(screen)
local clients = visible(screen)
local tclients = {}
-- Remove floating clients
for k, c in pairs(clients) do
if not floating.get(c) then
table.insert(tclients, c)
end
end
return tclients
end
--- Get a client by its relative index to the focused window.
-- @usage Set i to 1 to get next, -1 to get previous.
-- @param i The index.
-- @param c Optional client.
-- @return A client, or nil if no client is available.
function next(i, c)
-- Get currently focused client
local sel = c or capi.client.focus
if sel then
-- Get all visible clients
local cls = visible(sel.screen)
local fcls = {}
-- Remove all non-normal clients
for idx, c in ipairs(cls) do
if focus.filter(c) or c == sel then
table.insert(fcls, c)
end
end
cls = fcls
-- Loop upon each client
for idx, c in ipairs(cls) do
if c == sel then
-- Cycle
return cls[util.cycle(#cls, idx + i)]
end
end
end
end
-- Return true whether client B is in the right direction
-- compared to client A.
-- @param dir The direction.
-- @param cA The first client.
-- @param cB The second client.
-- @return True if B is in the direction of A.
local function is_in_direction(dir, cA, cB)
local gA = cA:geometry()
local gB = cB:geometry()
if dir == "up" then
return gA.y > gB.y
elseif dir == "down" then
return gA.y < gB.y
elseif dir == "left" then
return gA.x > gB.x
elseif dir == "right" then
return gA.x < gB.x
end
return false
end
-- Calculate distance between two points.
-- i.e: if we want to move to the right, we will take the right border
-- of the currently focused client and the left side of the checked client.
-- This avoid the focus of an upper client when you move to the right in a
-- tilebottom layout with nmaster=2 and 5 clients open, for instance.
-- @param dir The direction.
-- @param cA The first client.
-- @param cB The second client.
-- @return The distance between the clients.
local function calculate_distance(dir, cA, cB)
local gA = cA:geometry()
local gB = cB:geometry()
if dir == "up" then
gB.y = gB.y + gB.height
elseif dir == "down" then
gA.y = gA.y + gA.height
elseif dir == "left" then
gB.x = gB.x + gB.width
elseif dir == "right" then
gA.x = gA.x + gA.width
end
return math.sqrt(math.pow(gB.x - gA.x, 2) + math.pow(gB.y - gA.y, 2))
end
-- Get the nearest client in the given direction.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client to get a client relative to. Else focussed is used.
local function get_client_in_direction(dir, c)
local sel = c or capi.client.focus
if sel then
local geometry = sel:geometry()
local dist, dist_min
local target = nil
local cls = visible(sel.screen)
-- We check each client.
for i, c in ipairs(cls) do
-- Check geometry to see if client is located in the right direction.
if is_in_direction(dir, sel, c) then
-- Calculate distance between focused client and checked client.
dist = calculate_distance(dir, sel, c)
-- If distance is shorter then keep the client.
if not target or dist < dist_min then
target = c
dist_min = dist
end
end
end
return target
end
end
--- Focus a client by the given direction.
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client.
function focus.bydirection(dir, c)
local sel = c or capi.client.focus
if sel then
local target = get_client_in_direction(dir, sel)
-- If we found a client to focus, then do it.
if target then
capi.client.focus = target
end
end
end
--- Focus a client by its relative index.
-- @param i The index.
-- @param c Optional client.
function focus.byidx(i, c)
local target = next(i, c)
if target then
capi.client.focus = target
end
end
--- Swap a client with another client in the given direction
-- @param dir The direction, can be either "up", "down", "left" or "right".
-- @param c Optional client.
function swap.bydirection(dir, c)
local sel = c or capi.client.focus
if sel then
local target = get_client_in_direction(dir, sel)
-- If we found a client to swap with, then go for it
if target then
target:swap(sel)
end
end
end
--- Swap a client by its relative index.
-- @param i The index.
-- @param c Optional client, otherwise focused one is used.
function swap.byidx(i, c)
local sel = c or capi.client.focus
local target = next(i, sel)
if target then
target:swap(sel)
end
end
--- Cycle clients.
-- @param clockwise True to cycle clients clockwise.
-- @param screen Optional screen where to cycle clients.
function cycle(clockwise, screen)
local screen = screen or capi.mouse.screen
local cls = visible(screen)
-- We can't rotate without at least 2 clients, buddy.
if #cls >= 2 then
local c = table.remove(cls, 1)
if clockwise then
for i = #cls, 1, -1 do
c:swap(cls[i])
end
else
for _, rc in pairs(cls) do
c:swap(rc)
end
end
end
end
--- Get the master window.
-- @param screen Optional screen number, otherwise screen mouse is used.
-- @return The master window.
function getmaster(screen)
local s = screen or capi.mouse.screen
return visible(s)[1]
end
--- Set the client as slave: put it at the end of other windows.
-- @param c The window to set as slave.
function setslave(c)
local cls = visible(c.screen)
for k, v in pairs(cls) do
c:swap(v)
end
end
--- Move/resize a client relative to current coordinates.
-- @param x The relative x coordinate.
-- @param y The relative y coordinate.
-- @param w The relative width.
-- @param h The relative height.
-- @param c The optional client, otherwise focused one is used.
function moveresize(x, y, w, h, c)
local sel = c or capi.client.focus
local geometry = sel:geometry()
geometry['x'] = geometry['x'] + x
geometry['y'] = geometry['y'] + y
geometry['width'] = geometry['width'] + w
geometry['height'] = geometry['height'] + h
sel:geometry(geometry)
end
--- Move a client to a tag.
-- @param target The tag to move the client to.
-- @param c Optional client to move, otherwise the focused one is used.
function movetotag(target, c)
local sel = c or capi.client.focus
if sel and target.screen then
-- Set client on the same screen as the tag.
sel.screen = target.screen
sel:tags({ target })
end
end
--- Toggle a tag on a client.
-- @param target The tag to toggle.
-- @param c Optional client to toggle, otherwise the focused one is used.
function toggletag(target, c)
local sel = c or capi.client.focus
-- Check that tag and client screen are identical
if sel and sel.screen == target.screen then
local tags = sel:tags()
local index = nil;
for i, v in ipairs(tags) do
if v == target then
index = i
break
end
end
if index then
-- If it's the only tag for the window, stop.
if #tags == 1 then return end
tags[index] = nil
else
tags[#tags + 1] = target
end
sel:tags(tags)
end
end
--- Move a client to a screen. Default is next screen, cycling.
-- @param c The client to move.
-- @param s The screen number, default to current + 1.
function movetoscreen(c, s)
local sel = c or capi.client.focus
if sel then
local sc = capi.screen.count()
if not s then
s = sel.screen + 1
end
if s > sc then s = 1 elseif s < 1 then s = sc end
sel.screen = s
capi.mouse.coords(capi.screen[s].geometry)
capi.client.focus = sel
end
end
--- Mark a client, and then call 'marked' hook.
-- @param c The client to mark, the focused one if not specified.
-- @return True if the client has been marked. False if the client was already marked.
function mark(c)
local cl = c or capi.client.focus
if cl then
for k, v in pairs(data.marked) do
if cl == v then
return false
end
end
table.insert(data.marked, cl)
-- Call callback
cl:emit_signal("marked")
return true
end
end
--- Unmark a client and then call 'unmarked' hook.
-- @param c The client to unmark, or the focused one if not specified.
-- @return True if the client has been unmarked. False if the client was not marked.
function unmark(c)
local cl = c or capi.client.focus
for k, v in pairs(data.marked) do
if cl == v then
table.remove(data.marked, k)
cl:emit_signal("unmarked")
return true
end
end
return false
end
--- Check if a client is marked.
-- @param c The client to check, or the focused one otherwise.
function ismarked(c)
local cl = c or capi.client.focus
if cl then
for k, v in pairs(data.marked) do
if cl == v then
return true
end
end
end
return false
end
--- Toggle a client as marked.
-- @param c The client to toggle mark.
function togglemarked(c)
local cl = c or capi.client.focus
if not mark(c) then
unmark(c)
end
end
--- Return the marked clients and empty the marked table.
-- @return A table with all marked clients.
function getmarked()
for k, v in pairs(data.marked) do
v:emit_signal("unmarked")
end
t = data.marked
data.marked = {}
return t
end
--- Set a client floating state, overriding auto-detection.
-- Floating client are not handled by tiling layouts.
-- @param c A client.
-- @param s True or false.
function floating.set(c, s)
local c = c or capi.client.focus
if c and property.get(c, "floating") ~= s then
property.set(c, "floating", s)
local screen = c.screen
if s == true then
c:geometry(property.get(c, "floating_geometry"))
end
c.screen = screen
end
end
local function store_floating_geometry(c)
if floating.get(c) then
property.set(c, "floating_geometry", c:geometry())
end
end
-- Store the initial client geometry.
capi.client.add_signal("new", function(c)
local function store_init_geometry(c)
property.set(c, "floating_geometry", c:geometry())
c:remove_signal("property::geometry", store_init_geometry)
end
c:add_signal("property::geometry", store_init_geometry)
end)
capi.client.add_signal("manage", function(c)
c:add_signal("property::geometry", store_floating_geometry)
end)
--- Return if a client has a fixe size or not.
-- @param c The client.
function isfixed(c)
local c = c or capi.client.focus
if not c then return end
local h = c.size_hints
if h.min_width and h.max_width
and h.max_height and h.min_height
and h.min_width > 0 and h.max_width > 0
and h.max_height > 0 and h.min_height > 0
and h.min_width == h.max_width
and h.min_height == h.max_height then
return true
end
return false
end
--- Get a client floating state.
-- @param c A client.
-- @return True or false. Note that some windows might be floating even if you
-- did not set them manually. For example, windows with a type different than
-- normal.
function floating.get(c)
local c = c or capi.client.focus
if c then
local value = property.get(c, "floating")
if value ~= nil then
return value
end
if c.type ~= "normal"
or c.fullscreen
or c.maximized_vertical
or c.maximized_horizontal
or isfixed(c) then
return true
end
return false
end
end
--- Toggle the floating state of a client between 'auto' and 'true'.
-- @param c A client.
function floating.toggle(c)
local c = c or capi.client.focus
-- If it has been set to floating
if floating.get(c) then
floating.set(c, false)
else
floating.set(c, true)
end
end
--- Remove the floating information on a client.
-- @param c The client.
function floating.delete(c)
floating.set(c, nil)
end
--- Restore (=unminimize) a random client.
-- @param s The screen to use.
-- @return True if some client was restored.
function restore(s)
local s = s or (capi.client.focus and capi.client.focus.screen) or capi.mouse.screen
local cls = capi.client.get(s)
local tags = tag.selectedlist(s)
local mcls = {}
for k, c in pairs(cls) do
local ctags = c:tags()
if c.minimized then
for k, t in ipairs(tags) do
if util.table.hasitem(ctags, t) then
c.minimized = false
return true
end
end
end
end
return false
end
-- Normalize a set of numbers to 1
-- @param set the set of numbers to normalize
-- @param num the number of numbers to normalize
local function normalize(set, num)
local num = num or #set
local total = 0
if num then
for i = 1,num do
total = total + set[i]
end
for i = 1,num do
set[i] = set[i] / total
end
else
for i,v in ipairs(set) do
total = total + v
end
for i,v in ipairs(set) do
set[i] = v / total
end
end
end
--- Calculate a client's column number, index in that column, and
-- number of visible clients in this column.
-- @param c the client
-- @return col the column number
-- @return idx index of the client in the column
-- @return num the number of visible clients in the column
function idx(c)
local c = c or capi.client.focus
if not c then return end
local clients = tiled(c.screen)
local idx = nil
for k, cl in ipairs(clients) do
if cl == c then
idx = k
break
end
end
local t = tag.selected(c.screen)
local nmaster = tag.getnmaster(t)
if idx <= nmaster then
return {idx = idx, col=0, num=nmaster}
end
local nother = #clients - nmaster
idx = idx - nmaster
-- rather than regenerate the column number we can calculate it
-- based on the how the tiling algorithm places clients we calculate
-- the column, we could easily use the for loop in the program but we can
-- calculate it.
local ncol = tag.getncol(t)
-- minimum number of clients per column
local percol = math.floor(nother / ncol)
-- number of columns with an extra client
local overcol = math.mod(nother, ncol)
-- number of columns filled with [percol] clients
local regcol = ncol - overcol
local col = math.floor( (idx - 1) / percol) + 1
if col > regcol then
-- col = math.floor( (idx - (percol*regcol) - 1) / (percol + 1) ) + regcol + 1
-- simplified
col = math.floor( (idx + regcol + percol) / (percol+1) )
-- calculate the index in the column
idx = idx - percol*regcol - (col - regcol - 1) * (percol+1)
percol = percol+1
else
idx = idx - percol*(col-1)
end
return {idx = idx, col=col, num=percol}
end
--- Set the window factor of a client
-- @param wfact the window factor value
-- @param c the client
function setwfact(wfact, c)
-- get the currently selected window
local c = c or capi.client.focus
if not c or not c:isvisible() then return end
local t = tag.selected(c.screen)
local w = idx(c)
local cls = tiled(t.screen)
local nmaster = tag.getnmaster(t)
-- n is the number of windows currently visible for which we have to be concerned with the properties
local data = tag.getproperty(t, "windowfact") or {}
local colfact = data[w.col]
colfact[w.idx] = wfact
rest = 1-wfact
-- calculate the current denominator
local total = 0
for i = 1,w.num do
if i ~= w.idx then
total = total + colfact[i]
end
end
-- normalize the windows
for i = 1,w.num do
if i ~= w.idx then
colfact[i] = (colfact[i] * rest) / total
end
end
t:emit_signal("property::windowfact")
end
--- Increment a client's window factor
-- @param add amount to increase the client's window
-- @param c the client
function incwfact(add, c)
local c = c or capi.client.focus
if not c then return end
local t = tag.selected(c.screen)
local w = idx(c)
local nmaster = tag.getnmaster(t)
local data = tag.getproperty(t, "windowfact") or {}
local colfact = data[w.col]
curr = colfact[w.idx] or 1
colfact[w.idx] = curr + add
-- keep our ratios normalized
normalize(colfact, w.num)
t:emit_signal("property::windowfact")
end
--- Get a client dockable state.
-- @param c A client.
-- @return True or false. Note that some windows might be dockable even if you
-- did not set them manually. For example, windows with a type "utility", "toolbar"
-- or "dock"
function dockable.get(c)
local value = property.get(c, "dockable")
-- Some sane defaults
if value == nil then
if (c.type == "utility" or c.type == "toolbar" or c.type == "dock") then
value = true
else
value = false
end
end
return value
end
--- Set a client dockable state, overriding auto-detection.
-- With this enabled you can dock windows by moving them from the center
-- to the edge of the workarea.
-- @param c A client.
-- @param value True or false.
function dockable.set(c, value)
property.set(c, "dockable", value)
end
--- Get a client property.
-- @param c The client.
-- @param prop The property name.
-- @return The property.
function property.get(c, prop)
if data.properties[c] then
return data.properties[c][prop]
end
end
--- Set a client property.
-- This properties are internal to awful. Some are used to move clients, etc.
-- @param c The client.
-- @param prop The property name.
-- @param value The value.
function property.set(c, prop, value)
if not data.properties[c] then
data.properties[c] = {}
end
data.properties[c][prop] = value
c:emit_signal("property::" .. prop)
end
-- Register standards signals
capi.client.add_signal("focus", focus.history.add)
capi.client.add_signal("unmanage", focus.history.delete)
capi.client.add_signal("manage", function(c) c:add_signal("property::urgent", urgent.add) end)
capi.client.add_signal("focus", urgent.delete)
capi.client.add_signal("unmanage", urgent.delete)
capi.client.add_signal("unmanage", floating.delete)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,191 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @author Sébastien Gross &lt;seb-awesome@chezwam.org&gt;
-- @copyright 2008 Julien Danjou, Sébastien Gross
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local io = io
local os = os
local table = table
local math = math
local print = print
local util = require("awful.util")
--- Completion module.
-- This module store a set of function using shell to complete commands name.
module("awful.completion")
-- mapping of command/completion function
local bashcomp_funcs = {}
local bashcomp_src = "/etc/bash_completion"
--- Enable programmable bash completion in awful.completion.bash at the price of
-- a slight overhead.
-- @param src The bash completion source file, /etc/bash_completion by default.
function bashcomp_load(src)
if src then bashcomp_src = src end
local c, err = io.popen("/usr/bin/env bash -c 'source " .. bashcomp_src .. "; complete -p'")
if c then
while true do
local line = c:read("*line")
if not line then break end
-- if a bash function is used for completion, register it
if line:match(".* -F .*") then
bashcomp_funcs[line:gsub(".* (%S+)$","%1")] = line:gsub(".*-F +(%S+) .*$", "%1")
end
end
c:close()
else
print(err)
end
end
local function bash_escape(str)
str = str:gsub(" ", "\\ ")
str = str:gsub("%[", "\\[")
str = str:gsub("%]", "\\]")
str = str:gsub("%(", "\\(")
str = str:gsub("%)", "\\)")
return str
end
--- Use shell completion system to complete command and filename.
-- @param command The command line.
-- @param cur_pos The cursor position.
-- @param ncomp The element number to complete.
-- @param shell The shell to use for completion (bash (default) or zsh).
-- @return The new command, the new cursor position, the table of all matches.
function shell(command, cur_pos, ncomp, shell)
local wstart = 1
local wend = 1
local words = {}
local cword_index = 0
local cword_start = 0
local cword_end = 0
local i = 1
local comptype = "file"
-- do nothing if we are on a letter, i.e. not at len + 1 or on a space
if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
return command, cur_pos
elseif #command == 0 then
return command, cur_pos
end
while wend <= #command do
wend = command:find(" ", wstart)
if not wend then wend = #command + 1 end
table.insert(words, command:sub(wstart, wend - 1))
if cur_pos >= wstart and cur_pos <= wend + 1 then
cword_start = wstart
cword_end = wend
cword_index = i
end
wstart = wend + 1
i = i + 1
end
if cword_index == 1 then
comptype = "command"
end
local shell_cmd
if shell == "zsh" or (not shell and os.getenv("SHELL"):match("zsh$")) then
if comptype == "file" then
shell_cmd = "/usr/bin/env zsh -c 'local -a res; res=( " .. words[cword_index] .. "* ); print -l -- ${res[@]}'"
else
-- check commands, aliases, builtins, functions and reswords
shell_cmd = "/usr/bin/env zsh -c 'local -a res; "..
"res=( "..
"\"${(k)commands[@]}\" \"${(k)aliases[@]}\" \"${(k)builtins[@]}\" \"${(k)functions[@]}\" \"${(k)reswords[@]}\" "..
"); "..
"print -l -- ${(M)res[@]:#"..words[cword_index].."*}'"
end
else
if bashcomp_funcs[words[1]] then
-- fairly complex command with inline bash script to get the possible completions
shell_cmd = "/usr/bin/env bash -c 'source " .. bashcomp_src .. "; " ..
"__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
"COMP_WORDS=(" .. command .."); COMP_LINE=\"" .. command .. "\"; " ..
"COMP_COUNT=" .. cur_pos .. "; COMP_CWORD=" .. cword_index-1 .. "; " ..
bashcomp_funcs[words[1]] .. "; __print_completions'"
else
shell_cmd = "/usr/bin/env bash -c 'compgen -A " .. comptype .. " " .. words[cword_index] .. "'"
end
end
local c, err = io.popen(shell_cmd .. " | sort -u")
local output = {}
i = 0
if c then
while true do
local line = c:read("*line")
if not line then break end
if os.execute("test -d " .. line) == 0 then
line = line .. "/"
end
table.insert(output, bash_escape(line))
end
c:close()
else
print(err)
end
-- no completion, return
if #output == 0 then
return command, cur_pos
end
-- cycle
while ncomp > #output do
ncomp = ncomp - #output
end
local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
cur_pos = cword_end + #output[ncomp] + 1
return str, cur_pos, output
end
--- Run a generic completion.
-- For this function to run properly the awful.completion.keyword table should
-- be fed up with all keywords. The completion is run against these keywords.
-- @param text The current text the user had typed yet.
-- @param cur_pos The current cursor position.
-- @param ncomp The number of yet requested completion using current text.
-- @param keywords The keywords table uised for completion.
-- @return The new match, the new cursor position, the table of all matches.
function generic(text, cur_pos, ncomp, keywords)
-- The keywords table may be empty
if #keywords == 0 then
return text, #text + 1
end
-- if no text had been typed yet, then we could start cycling around all
-- keywords with out filtering and move the cursor at the end of keyword
if text == nil or #text == 0 then
ncomp = math.mod(ncomp - 1, #keywords) + 1
return keywords[ncomp], #keywords[ncomp] + 2
end
-- Filter out only keywords starting with text
local matches = {}
table.foreach(keywords, function(_, x)
if x:sub(1 , #text) == text then
table.insert(matches, x)
end
end)
-- if there are no matches just leave out with the current text and position
if #matches == 0 then
return text, #text + 1, matches
end
-- cycle around all matches
ncomp = math.mod(ncomp - 1, #matches) + 1
return matches[ncomp], #matches[ncomp] + 1, matches
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,19 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local dbus = dbus
--- D-Bus module for awful.
-- This module simply request the org.naquadah.awesome.awful name on the D-Bus
-- for futur usage by other awful modules.
module("awful.dbus")
if dbus then
dbus.request_name("session", "org.naquadah.awesome.awful")
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,160 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local pairs = pairs
local table = table
local ipairs = ipairs
local type = type
local math = math
local capi =
{
hooks = hooks
}
local util = require("awful.util")
--- Hooks module for awful.
-- This module is deprecated and should not be used anymore. You are encouraged
-- to use signals.
module("awful.hooks")
-- User hook functions
user = {}
--- Create a new userhook (for external libs).
-- @param name Hook name.
function user.create(name)
_M[name] = {}
_M[name].callbacks = {}
_M[name].register = function (f)
table.insert(_M[name].callbacks, f)
end
_M[name].unregister = function (f)
for k, h in ipairs(_M[name].callbacks) do
if h == f then
table.remove(_M[name].callbacks, k)
break
end
end
end
end
--- Call a created userhook (for external libs).
-- @param name Hook name.
function user.call(name, ...)
for name, callback in pairs(_M[name].callbacks) do
callback(...)
end
end
-- Autodeclare awful.hooks.* functions
-- mapped to awesome hooks.* functions
for name, hook in pairs(capi.hooks) do
_M[name] = {}
if name == 'timer' then
_M[name].register = function (time, f, runnow)
util.deprecate("timer object")
if type(time) ~= 'number' or type(f) ~= 'function' or time <= 0 then
return
end
if not _M[name].callbacks then
_M[name].callbacks = {}
end
for k, v in pairs(_M[name].callbacks) do
if v.callback == f then
_M[name].unregister(f)
_M[name].register(time, f, runnow)
return
end
end
local new_timer
if _M[name].timer then
-- Take the smallest between current and new
new_timer = math.min(time, _M[name].timer)
else
new_timer = time
end
if _M[name].timer ~= new_timer then
_M[name].timer = new_timer
end
hook(_M[name].timer, function (...)
for i, callback in ipairs(_M[name].callbacks) do
callback['counter'] = callback['counter'] + _M[name].timer
if callback['counter'] >= callback['timer'] then
callback['callback'](...)
callback['counter'] = 0
end
end
end)
if runnow then
table.insert(_M[name].callbacks, { callback = f, timer = time, counter = time })
else
table.insert(_M[name].callbacks, { callback = f, timer = time, counter = 0 })
end
end
_M[name].unregister = function (f)
if _M[name].callbacks then
for k, h in ipairs(_M[name].callbacks) do
if h.callback == f then
table.remove(_M[name].callbacks, k)
break
end
end
local delays = { }
for k, h in ipairs(_M[name].callbacks) do
table.insert(delays, h.timer)
end
table.sort(delays)
_M[name].timer = delays[1]
if not delays[1] then delays[1] = 0 end
hook(delays[1], function (...)
for i, callback in ipairs(_M[name].callbacks) do
callback['counter'] = callback['counter'] + _M[name].timer
if callback['counter'] >= callback['timer'] then
callback['callback'](...)
callback['counter'] = 0
end
end
end)
end
end
else
_M[name].register = function (f)
util.deprecate("signals")
if not _M[name].callbacks then
_M[name].callbacks = {}
hook(function (...)
for i, callback in ipairs(_M[name].callbacks) do
callback(...)
end
end)
end
table.insert(_M[name].callbacks, f)
end
end
if name ~= "timer" then
_M[name].unregister = function (f)
if _M[name].callbacks then
for k, h in ipairs(_M[name].callbacks) do
if h == f then
table.remove(_M[name].callbacks, k)
break
end
end
end
end
end
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,30 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
require("awful.client")
require("awful.completion")
require("awful.hooks")
require("awful.layout")
require("awful.placement")
require("awful.prompt")
require("awful.screen")
require("awful.tag")
require("awful.titlebar")
require("awful.util")
require("awful.widget")
require("awful.menu")
require("awful.mouse")
require("awful.remote")
require("awful.key")
require("awful.button")
require("awful.wibox")
require("awful.startup_notification")
require("awful.tooltip")
--- AWesome Functions very UsefuL
module("awful")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,78 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local setmetatable = setmetatable
local ipairs = ipairs
local capi = { key = key }
local util = require("awful.util")
--- Create easily new key objects ignoring certain modifiers.
module("awful.key")
--- Modifiers to ignore.
-- By default this is initialized as { "Lock", "Mod2" }
-- so the Caps Lock or Num Lock modifier are not taking into account by awesome
-- when pressing keys.
-- @name ignore_modifiers
-- @class table
ignore_modifiers = { "Lock", "Mod2" }
--- Create a new key to use as binding.
-- This function is useful to create several keys from one, because it will use
-- the ignore_modifier variable to create more key with or without the ignored
-- modifiers activated.
-- For example if you want to ignore CapsLock in your keybinding (which is
-- ignored by default by this function), creating key binding with this function
-- will return 2 key objects: one with CapsLock on, and the other one with
-- CapsLock off.
-- @see capi.key
-- @return A table with one or several key objects.
function new(mod, key, press, release)
local ret = {}
local subsets = util.subsets(ignore_modifiers)
for _, set in ipairs(subsets) do
ret[#ret + 1] = capi.key({ modifiers = util.table.join(mod, set),
key = key })
if press then
ret[#ret]:add_signal("press", function(kobj, ...) press(...) end)
end
if release then
ret[#ret]:add_signal("release", function(kobj, ...) release(...) end)
end
end
return ret
end
--- Compare a key object with modifiers and key.
-- @param key The key object.
-- @param pressed_mod The modifiers to compare with.
-- @param pressed_key The key to compare with.
function match(key, pressed_mod, pressed_key)
-- First, compare key.
if pressed_key ~= key.key then return false end
-- Then, compare mod
local mod = key.modifiers
-- For each modifier of the key object, check that the modifier has been
-- pressed.
for _, m in ipairs(mod) do
-- Has it been pressed?
if not util.table.hasitem(pressed_mod, m) then
-- No, so this is failure!
return false
end
end
-- If the number of pressed modifier is ~=, it is probably >, so this is not
-- the same, return false.
if #pressed_mod ~= #mod then
return false
end
return true
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,156 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local type = type
local capi = { screen = screen, client = client }
local tag = require("awful.tag")
local util = require("awful.util")
local suit = require("awful.layout.suit")
local ascreen = require("awful.screen")
local capi = {
screen = screen,
awesome = awesome,
client = client
}
local client = require("awful.client")
--- Layout module for awful
module("awful.layout")
-- This is a special lock used by the arrange function.
-- This avoids recurring call by emitted signals.
local arrange_lock = false
--- Get the current layout.
-- @param screen The screen number.
-- @return The layout function.
function get(screen)
local t = tag.selected(screen)
return tag.getproperty(t, "layout") or suit.floating
end
--- Change the layout of the current tag.
-- @param layouts A table of layouts.
-- @param i Relative index.
function inc(layouts, i)
local t = tag.selected()
if t then
local curlayout = get()
local curindex
local rev_layouts = {}
for k, v in ipairs(layouts) do
if v == curlayout then
curindex = k
break
end
end
if curindex then
local newindex = util.cycle(#layouts, curindex + i)
set(layouts[newindex])
end
end
end
--- Set the layout function of the current tag.
-- @param layout Layout name.
function set(layout, t)
t = t or tag.selected()
tag.setproperty(t, "layout", layout)
end
--- Arrange a screen using its current layout.
-- @param screen The screen to arrange.
function arrange(screen)
if arrange_lock then return end
arrange_lock = true
local p = {}
p.workarea = capi.screen[screen].workarea
-- Handle padding
local padding = ascreen.padding(capi.screen[screen])
if padding then
p.workarea.x = p.workarea.x + (padding.left or 0)
p.workarea.y = p.workarea.y + (padding.top or 0)
p.workarea.width = p.workarea.width - ((padding.left or 0 ) + (padding.right or 0))
p.workarea.height = p.workarea.height - ((padding.top or 0) + (padding.bottom or 0))
end
p.geometry = capi.screen[screen].geometry
p.clients = client.tiled(screen)
p.screen = screen
get(screen).arrange(p)
capi.screen[screen]:emit_signal("arrange")
arrange_lock = false
end
--- Get the current layout name.
-- @param layout The layout.
-- @return The layout name.
function getname(layout)
local layout = layout or get()
return layout.name
end
local function arrange_prop(obj) arrange(obj.screen) end
capi.client.add_signal("new", function(c)
c:add_signal("property::size_hints_honor", arrange_prop)
c:add_signal("property::struts", arrange_prop)
c:add_signal("property::minimized", arrange_prop)
c:add_signal("property::sticky", arrange_prop)
c:add_signal("property::fullscreen", arrange_prop)
c:add_signal("property::maximized_horizontal", arrange_prop)
c:add_signal("property::maximized_vertical", arrange_prop)
c:add_signal("property::border_width", arrange_prop)
c:add_signal("property::hidden", arrange_prop)
c:add_signal("property::titlebar", arrange_prop)
c:add_signal("property::floating", arrange_prop)
c:add_signal("property::geometry", arrange_prop)
-- If prop is screen, we do not know what was the previous screen, so
-- let's arrange all screens :-(
c:add_signal("property::screen", function(c)
for screen = 1, capi.screen.count() do arrange(screen) end end)
end)
local function arrange_on_tagged(c, tag)
if not tag.screen then return end
arrange(tag.screen)
if not capi.client.focus or not capi.client.focus:isvisible() then
local c = client.focus.history.get(tag.screen, 0)
if c then capi.client.focus = c end
end
end
for s = 1, capi.screen.count() do
tag.attached_add_signal(s, "property::mwfact", arrange_prop)
tag.attached_add_signal(s, "property::nmaster", arrange_prop)
tag.attached_add_signal(s, "property::ncol", arrange_prop)
tag.attached_add_signal(s, "property::layout", arrange_prop)
tag.attached_add_signal(s, "property::windowfact", arrange_prop)
tag.attached_add_signal(s, "property::selected", arrange_prop)
tag.attached_add_signal(s, "tagged", arrange_prop)
capi.screen[s]:add_signal("property::workarea", function(screen)
arrange(screen.index)
end)
capi.screen[s]:add_signal("tag::attach", function (screen, tag)
arrange(screen.index)
end)
capi.screen[s]:add_signal("tag::detach", function (screen, tag)
arrange(screen.index)
end)
capi.screen[s]:add_signal("padding", function (screen)
arrange(screen.index)
end)
end
capi.client.add_signal("focus", function(c) arrange(c.screen) end)
capi.client.add_signal("list", function()
for screen = 1, capi.screen.count() do
arrange(screen)
end
end)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,74 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local math = math
--- Fair layouts module for awful
module("awful.layout.suit.fair")
local function fair(p, orientation)
local wa = p.workarea
local cls = p.clients
if #cls > 0 then
local cells = math.ceil(math.sqrt(#cls))
local strips = math.ceil(#cls / cells)
local cell = 0
local strip = 0
for k, c in ipairs(cls) do
local g = {}
if ( orientation == "east" and #cls > 2 )
or ( orientation == "south" and #cls <= 2 ) then
if #cls < (strips * cells) and strip == strips - 1 then
g.width = wa.width / (cells - ((strips * cells) - #cls))
else
g.width = wa.width / cells
end
g.height = wa.height / strips
g.x = wa.x + cell * g.width
g.y = wa.y + strip * g.height
else
if #cls < (strips * cells) and strip == strips - 1 then
g.height = wa.height / (cells - ((strips * cells) - #cls))
else
g.height = wa.height / cells
end
g.width = wa.width / strips
g.x = wa.x + strip * g.width
g.y = wa.y + cell * g.height
end
c:geometry(g)
cell = cell + 1
if cell == cells then
cell = 0
strip = strip + 1
end
end
end
end
--- Horizontal fair layout.
-- @param screen The screen to arrange.
horizontal = {}
horizontal.name = "fairh"
function horizontal.arrange(p)
return fair(p, "east")
end
-- Vertical fair layout.
-- @param screen The screen to arrange.
name = "fairv"
function arrange(p)
return fair(p, "south")
end

View File

@ -0,0 +1,13 @@
---------------------------------------------------------------------------
-- @author Gregor Best
-- @copyright 2008 Gregor Best
-- @release v3.4.10
---------------------------------------------------------------------------
--- Dummy function for floating layout
module("awful.layout.suit.floating")
function arrange()
end
name = "floating"

View File

@ -0,0 +1,9 @@
require("awful.layout.suit.max")
require("awful.layout.suit.tile")
require("awful.layout.suit.fair")
require("awful.layout.suit.floating")
require("awful.layout.suit.magnifier")
require("awful.layout.suit.spiral")
--- Suits for awful
module("awful.layout.suit")

View File

@ -0,0 +1,92 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local math = math
local tag = require("awful.tag")
local capi =
{
client = client,
screen = screen
}
local client = require("awful.client")
--- Magnifier layout module for awful
module("awful.layout.suit.magnifier")
function arrange(p)
-- Fullscreen?
local area = p.workarea
local cls = p.clients
local focus = capi.client.focus
local mwfact = tag.getmwfact(tag.selected(p.screen))
local fidx
-- Check that the focused window is on the right screen
if focus and focus.screen ~= p.screen then focus = nil end
if not focus and #cls > 0 then
focus = cls[1]
fidx = 1
end
-- If focused window is not tiled, take the first one which is tiled.
if client.floating.get(focus) then
focus = cls[1]
fidx = 1
end
-- Abort if no clients are present
if not focus then return end
local geometry = {}
if #cls > 1 then
geometry.width = area.width * math.sqrt(mwfact)
geometry.height = area.height * math.sqrt(mwfact)
geometry.x = area.x + (area.width - geometry.width) / 2
geometry.y = area.y + (area.height - geometry.height) /2
else
geometry.x = area.x
geometry.y = area.y
geometry.width = area.width
geometry.height = area.height
end
focus:geometry(geometry)
focus:raise()
if #cls > 1 then
geometry.x = area.x
geometry.y = area.y
geometry.height = area.height / (#cls - 1)
geometry.width = area.width
-- We don't know what the focus window index. Try to find it.
if not fidx then
for k, c in ipairs(cls) do
if c == focus then
fidx = k
break
end
end
end
-- First move clients that are before focused client.
for k = fidx + 1, #cls do
cls[k]:geometry(geometry)
geometry.y = geometry.y + geometry.height
end
-- Then move clients that are after focused client.
-- So the next focused window will be the one at the top of the screen.
for k = 1, fidx - 1 do
cls[k]:geometry(geometry)
geometry.y = geometry.y + geometry.height
end
end
end
name = "magnifier"

View File

@ -0,0 +1,41 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local pairs = pairs
local client = require("awful.client")
--- Maximized and fullscreen layouts module for awful
module("awful.layout.suit.max")
local function fmax(p, fs)
-- Fullscreen?
local area
if fs then
area = p.geometry
else
area = p.workarea
end
for k, c in pairs(p.clients) do
c:geometry(area)
end
end
--- Maximized layout.
-- @param screen The screen to arrange.
name = "max"
function arrange(p)
return fmax(p, false)
end
--- Fullscreen layout.
-- @param screen The screen to arrange.
fullscreen = {}
fullscreen.name = "fullscreen"
function fullscreen.arrange(p)
return fmax(p, true)
end

View File

@ -0,0 +1,58 @@
---------------------------------------------------------------------------
-- @author Uli Schlachter &lt;psychon@znc.in&gt;
-- @copyright 2009 Uli Schlachter
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
module("awful.layout.suit.spiral")
local function spiral(p, spiral)
local wa = p.workarea
local cls = p.clients
local n = #cls
for k, c in ipairs(cls) do
if k < n then
if k % 2 == 0 then
wa.height = wa.height / 2
else
wa.width = wa.width / 2
end
end
if k % 4 == 0 and spiral then
wa.x = wa.x - wa.width
elseif k % 2 == 0 or
(k % 4 == 3 and k < n and spiral) then
wa.x = wa.x + wa.width
end
if k % 4 == 1 and k ~= 1 and spiral then
wa.y = wa.y - wa.height
elseif k % 2 == 1 and k ~= 1 or
(k % 4 == 0 and k < n and spiral) then
wa.y = wa.y + wa.height
end
c:geometry(wa)
end
end
--- Dwindle layout
dwindle = {}
dwindle.name = "dwindle"
function dwindle.arrange(p)
return spiral(p, false)
end
--- Spiral layout
name = "spiral"
function arrange(p)
return spiral(p, true)
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,180 @@
---------------------------------------------------------------------------
-- @author Donald Ephraim Curtis &lt;dcurtis@cs.uiowa.edu&gt;
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Donald Ephraim Curtis
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local math = math
local tag = require("awful.tag")
--- Tiled layouts module for awful
module("awful.layout.suit.tile")
local function tile_group(cls, wa, orientation, fact, group)
-- get our orientation right
local height = "height"
local width = "width"
local x = "x"
local y = "y"
if orientation == "top" or orientation == "bottom" then
height = "width"
width = "height"
x = "y"
y = "x"
end
-- make this more generic (not just width)
available = wa[width] - (group.coord - wa[x])
-- find our total values
local total_fact = 0
local min_fact = 1
local size = group.size
for c = group.first,group.last do
-- determine the width/height based on the size_hint
local i = c - group.first +1
local size_hints = cls[c].size_hints
local size_hint = size_hints["min_"..width] or size_hints["base_"..width] or 0
size_hint = size_hint + cls[c].border_width*2
size = math.max(size_hint, size)
-- calculate the height
if not fact[i] then
fact[i] = min_fact
else
min_fact = math.min(fact[i],min_fact)
end
total_fact = total_fact + fact[i]
end
size = math.min(size, available)
local coord = wa[y]
local geom = {}
local used_size = 0
local unused = wa[height]
for c = group.first,group.last do
local i = c - group.first +1
geom[width] = size
geom[height] = math.floor(unused * fact[i] / total_fact)
geom[x] = group.coord
geom[y] = coord
geom = cls[c]:geometry(geom)
coord = coord + geom[height]
unused = unused - geom[height]
total_fact = total_fact - fact[i]
used_size = math.max(used_size, geom[width])
end
return used_size
end
local function tile(param, orientation)
local t = tag.selected(param.screen)
orientation = orientation or "right"
-- this handles are different orientations
local height = "height"
local width = "width"
local x = "x"
local y = "y"
if orientation == "top" or orientation == "bottom" then
height = "width"
width = "height"
x = "y"
y = "x"
end
local cls = param.clients
local nmaster = math.min(tag.getnmaster(t), #cls)
local nother = math.max(#cls - nmaster,0)
local mwfact = tag.getmwfact(t)
local wa = param.workarea
local ncol = tag.getncol(t)
local data = tag.getdata(t).windowfact
if not data then
data = {}
tag.getdata(t).windowfact = data
end
local coord = wa[x]
local place_master = true
if orientation == "left" or orientation == "top" then
-- if we are on the left or top we need to render the other windows first
place_master = false
end
-- this was easier than writing functions because there is a lot of data we need
for d = 1,2 do
if place_master and nmaster > 0 then
local size = wa[width]
if nother > 0 then
size = math.min(wa[width] * mwfact, wa[width] - (coord - wa[x]))
end
if not data[0] then
data[0] = {}
end
coord = coord + tile_group(cls, wa, orientation, data[0], {first=1, last=nmaster, coord = coord, size = size})
end
if not place_master and nother > 0 then
local last = nmaster
-- we have to modify the work area size to consider left and top views
local wasize = wa[width]
if nmaster > 0 and (orientation == "left" or orientation == "top") then
wasize = wa[width] - wa[width]*mwfact
end
for i = 1,ncol do
-- Try to get equal width among remaining columns
local size = math.min( (wasize - (coord - wa[x])) / (ncol - i + 1) )
local first = last + 1
last = last + math.floor((#cls - last)/(ncol - i + 1))
-- tile the column and update our current x coordinate
if not data[i] then
data[i] = {}
end
coord = coord + tile_group(cls, wa, orientation, data[i], { first = first, last = last, coord = coord, size = size })
end
end
place_master = not place_master
end
end
right = {}
right.name = "tile"
right.arrange = tile
--- The main tile algo, on left.
-- @param screen The screen number to tile.
left = {}
left.name = "tileleft"
function left.arrange(p)
return tile(p, "left")
end
--- The main tile algo, on bottom.
-- @param screen The screen number to tile.
bottom = {}
bottom.name = "tilebottom"
function bottom.arrange(p)
return tile(p, "bottom")
end
--- The main tile algo, on top.
-- @param screen The screen number to tile.
top = {}
top.name = "tiletop"
function top.arrange(p)
return tile(p, "top")
end
arrange = right.arrange
name = right.name

View File

@ -0,0 +1,455 @@
---------------------------------------------------------------------------
-- @author Damien Leone &lt;damien.leone@gmail.com&gt;
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Damien Leone, Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local pairs = pairs
local table = table
local string = string
local type = type
local setmetatable = setmetatable
local wibox = wibox
local image = image
local widget = widget
local button = require("awful.button")
local capi =
{
screen = screen,
mouse = mouse,
client = client,
keygrabber = keygrabber
}
local util = require("awful.util")
local tags = require("awful.tag")
local layout = require("awful.widget.layout")
local awbeautiful = require("beautiful")
local tonumber = tonumber
--- Creation of menus.
module("awful.menu")
local cur_menu
--- Key bindings for menu navigation.
-- Keys are: up, down, exec, back, close. Value are table with a list of valid
-- keys for the action, i.e. menu_keys.up = { "j", "k" } will bind 'j' and 'k'
-- key to up action. This is common to all created menu.
-- @class table
-- @name menu_keys
menu_keys = { up = { "Up" },
down = { "Down" },
exec = { "Return", "Right" },
back = { "Left" },
close = { "Escape" } }
local function load_theme(custom)
local theme = {}
local beautiful
beautiful = awbeautiful.get()
theme.fg_focus = custom.fg_focus or beautiful.menu_fg_focus or beautiful.fg_focus
theme.bg_focus = custom.bg_focus or beautiful.menu_bg_focus or beautiful.bg_focus
theme.fg_normal = custom.fg_normal or beautiful.menu_fg_normal or beautiful.fg_normal
theme.bg_normal = custom.bg_normal or beautiful.menu_bg_normal or beautiful.bg_normal
theme.submenu_icon = custom.submenu_icon or beautiful.menu_submenu_icon
theme.menu_height = custom.height or beautiful.menu_height or 16
theme.menu_width = custom.width or beautiful.menu_width or 100
theme.border = custom.border_color or beautiful.menu_border_color or beautiful.border_normal
theme.border_width = custom.border_width or beautiful.menu_border_width or beautiful.border_width
return theme
end
local function item_leave(menu, num)
if num > 0 then
menu.items[num].wibox.fg = menu.theme.fg_normal
menu.items[num].wibox.bg = menu.theme.bg_normal
end
end
--- Hide a menu popup.
-- @param menu The menu to hide.
function hide(menu)
-- Remove items from screen
for i = 1, #menu.items do
item_leave(menu, i)
menu.items[i].wibox.screen = nil
end
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
menu.sel = nil
if cur_menu == menu then
cur_menu = cur_menu.parent
end
if not cur_menu and menu.keygrabber then
capi.keygrabber.stop()
end
end
-- Get the elder parent so for example when you kill
-- it, it will destroy the whole family.
local function get_parents(menu)
if menu.parent then
return get_parents(menu.parent)
end
return menu
end
local function exec(menu, num, mouse_event)
local cmd = menu.items[num].cmd
if type(cmd) == "table" then
if #cmd == 0 then
return
end
if not menu.child[num] then
menu.child[num] = new({ items = cmd }, menu, num)
end
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
menu.active_child = menu.child[num]
menu.active_child:show()
elseif type(cmd) == "string" then
get_parents(menu):hide()
util.spawn(cmd)
elseif type(cmd) == "function" then
get_parents(menu):hide()
cmd(menu.items[num].returned_value)
end
end
local function item_enter(menu, num, mouse_event)
if menu.sel == num then
return
elseif menu.sel then
item_leave(menu, menu.sel)
end
menu.items[num].wibox.fg = menu.theme.fg_focus
menu.items[num].wibox.bg = menu.theme.bg_focus
menu.sel = num
cur_menu = menu
if menu.auto_expand and mouse_event then
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
if type(menu.items[num].cmd) == "table" then
exec(menu, num)
end
end
end
local function check_access_key(menu, key)
for i, item in pairs(menu.items) do
if item.akey == key then
item_enter(menu, i)
exec(menu, i)
return
end
end
if menu.parent then
check_access_key(menu.parent, key)
end
end
local function grabber(mod, key, event)
if event == "release" then
return true
end
local sel = cur_menu.sel or 0
if util.table.hasitem(menu_keys.up, key) then
local sel_new = sel-1 < 1 and #cur_menu.items or sel-1
item_enter(cur_menu, sel_new)
elseif util.table.hasitem(menu_keys.down, key) then
local sel_new = sel+1 > #cur_menu.items and 1 or sel+1
item_enter(cur_menu, sel_new)
elseif sel > 0 and util.table.hasitem(menu_keys.exec, key) then
exec(cur_menu, sel)
elseif util.table.hasitem(menu_keys.back, key) then
cur_menu:hide()
elseif util.table.hasitem(menu_keys.close, key) then
get_parents(cur_menu):hide()
else
check_access_key(cur_menu, key)
end
return true
end
local function add_item(data, num, item_info)
local item = wibox({
fg = data.theme.fg_normal,
bg = data.theme.bg_normal,
border_color = data.theme.border,
border_width = data.theme.border_width
})
-- Create bindings
local bindings = util.table.join(
button({}, 1, function () item_enter(data, num); exec(data, num) end),
button({}, 3, function () data:hide() end)
)
-- Create the item label widget
local label = widget({ type = "textbox" })
local key = ''
label.text = string.gsub(util.escape(item_info[1]), "&amp;(%w)",
function (l)
key = string.lower(l)
return "<u>"..l.."</u>"
end, 1)
-- Set icon if needed
local iconbox
if item_info[3] then
local icon = type(item_info[3]) == "string" and image(item_info[3]) or item_info[3]
if icon.width > data.h or icon.height > data.h then
local width, height
if ((data.h/icon.height) * icon.width) > data.h then
width, height = data.h, (data.h / icon.width) * icon.height
else
width, height = (data.h / icon.height) * icon.width, data.h
end
icon = icon:crop_and_scale(0, 0, icon.width, icon.height, width, height)
end
iconbox = widget { type = "imagebox" }
iconbox.image = icon
layout.margins[label] = { left = 2 }
else
layout.margins[label] = { left = data.h + 2 }
end
item:buttons(bindings)
local mouse_enter_func = function () item_enter(data, num, true) end
item:add_signal("mouse::enter", mouse_enter_func)
-- Create the submenu icon widget
local submenu
if type(item_info[2]) == "table" then
submenu = widget({ type = "imagebox" })
submenu.image = data.theme.submenu_icon and image(data.theme.submenu_icon)
submenu:buttons(bindings)
end
-- Add widgets to the wibox
if iconbox then
item.widgets = {
iconbox,
label,
{ submenu, layout = layout.horizontal.rightleft },
layout = layout.horizontal.leftright
}
else
item.widgets = {
label,
{ submenu, layout = layout.horizontal.rightleft },
layout = layout.horizontal.leftright
}
end
item.height = label:extents().height + 2
item.ontop = true
return { wibox = item, akey= key, cmd = item_info[2], returned_value=item_info[1] }
end
--- Build a popup menu with running clients and shows it.
-- @param menu Menu table, see new() function for more informations
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
-- @return The menu.
function clients(menu, args)
local cls = capi.client.get()
local cls_t = {}
for k, c in pairs(cls) do
cls_t[#cls_t + 1] = { util.escape(c.name) or "",
function ()
if not c:isvisible() then
tags.viewmore(c:tags(), c.screen)
end
capi.client.focus = c
c:raise()
end,
c.icon }
end
if not menu then
menu = {}
end
menu.items = cls_t
local m = new(menu)
m:show(args)
return m
end
local function set_coords(menu, screen_idx, m_coords)
local s_geometry = capi.screen[screen_idx].workarea
local screen_w = s_geometry.x + s_geometry.width
local screen_h = s_geometry.y + s_geometry.height
local i_h = menu.h + menu.theme.border_width
local m_h = (i_h * #menu.items) + menu.theme.border_width
if menu.parent then
menu.w = menu.parent.w
menu.h = menu.parent.h
local p_w = i_h * (menu.num - 1)
local m_w = menu.w - menu.theme.border_width
menu.y = menu.parent.y + p_w + m_h > screen_h and screen_h - m_h or menu.parent.y + p_w
menu.x = menu.parent.x + m_w*2 > screen_w and menu.parent.x - m_w or menu.parent.x + m_w
else
local m_w = menu.w
if m_coords == nil then
m_coords = capi.mouse.coords()
m_coords.x = m_coords.x + 1
m_coords.y = m_coords.y + 1
end
menu.y = m_coords.y < s_geometry.y and s_geometry.y or m_coords.y
menu.x = m_coords.x < s_geometry.x and s_geometry.x or m_coords.x
menu.y = menu.y + m_h > screen_h and screen_h - m_h or menu.y
menu.x = menu.x + m_w > screen_w and screen_w - m_w or menu.x
end
end
--- Show a menu.
-- @param menu The menu to show.
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
-- @param args.coords Menu position defaulting to mouse.coords()
function show(menu, args)
args = args or {}
local screen_index = capi.mouse.screen
local keygrabber = args.keygrabber or false
local coords = args.coords or nil
set_coords(menu, screen_index, coords)
for num, item in pairs(menu.items) do
local wibox = item.wibox
wibox.width = menu.w
wibox.height = menu.h
wibox.x = menu.x
wibox.y = menu.y + (num - 1) * (menu.h + wibox.border_width)
wibox.screen = screen_index
end
if menu.parent then
menu.keygrabber = menu.parent.keygrabber
elseif keygrabber ~= nil then
menu.keygrabber = keygrabber
else
menu.keygrabber = false
end
if not cur_menu and menu.keygrabber then
capi.keygrabber.run(grabber)
end
cur_menu = menu
end
--- Toggle menu visibility.
-- @param menu The menu to show if it's hidden, or to hide if it's shown.
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
-- @param args.coords Menu position {x,y}
function toggle(menu, args)
if menu.items[1] and menu.items[1].wibox.screen then
menu:hide()
else
menu:show(args)
end
end
--- Open a menu popup.
-- @param menu Table containing the menu informations.<br/>
-- <ul>
-- <li> Key items: Table containing the displayed items. Each element is a table containing: item name, triggered action, submenu table or function, item icon (optional). </li>
-- <li> Keys [fg|bg]_[focus|normal], border, border_width, submenu_icon, height and width override the default display for your menu, each of them are optional. </li>
-- <li> Key auto_expand controls the submenu auto expand behaviour by setting it to true (default) or false. </li>
-- </ul>
-- @param parent Specify the parent menu if we want to open a submenu, this value should never be set by the user.
-- @param num Specify the parent's clicked item number if we want to open a submenu, this value should never be set by the user.
-- @usage The following function builds, and shows a menu of clients that match
-- a particular rule. Bound to a key, it can for example be used to select from
-- dozens of terminals open on several tags. With the use of
-- <code>match_any</code> instead of <code>match</code>, menu of clients with
-- different classes can also be build.
--
-- <p><code>
-- function terminal_menu () <br/>
-- &nbsp; terms = {} <br/>
-- &nbsp; for i, c in pairs(client.get()) do <br/>
-- &nbsp;&nbsp; if awful.rules.match(c, {class = "URxvt"}) then <br/>
-- &nbsp;&nbsp;&nbsp; terms[i] = <br/>
-- &nbsp;&nbsp;&nbsp; {c.name, <br/>
-- &nbsp;&nbsp;&nbsp; function() <br/>
-- &nbsp;&nbsp;&nbsp;&nbsp; awful.tag.viewonly(c:tags()[1]) <br/>
-- &nbsp;&nbsp;&nbsp;&nbsp; client.focus = c <br/>
-- &nbsp;&nbsp;&nbsp; end, <br/>
-- &nbsp;&nbsp;&nbsp; c.icon <br/>
-- &nbsp;&nbsp;&nbsp; } <br/>
-- &nbsp;&nbsp; end <br/>
-- &nbsp; end <br/>
-- &nbsp; m = awful.menu({items = terms}) <br/>
-- &nbsp; m:show({keygrabber=true}) <br/>
-- &nbsp; return m <br/>
-- end <br/>
--</code></p>
function new(menu, parent, num)
-- Create a table to store our menu informations
local data = {}
data.items = {}
data.num = num or 1
data.theme = parent and parent.theme or load_theme(menu)
data.parent = parent
data.child = {}
if parent then
data.auto_expand = parent.auto_expand
elseif menu.auto_expand ~= nil then
data.auto_expand = menu.auto_expand
else
data.auto_expand = true
end
data.h = parent and parent.h or data.theme.menu_height
if type(data.h) ~= 'number' then data.h = tonumber(data.h) end
data.w = parent and parent.w or data.theme.menu_width
if type(data.w) ~= 'number' then data.w = tonumber(data.w) end
-- Create items
for k, v in pairs(menu.items) do
table.insert(data.items, add_item(data, k, v))
end
if #data.items > 0 and data.h < data.items[1].wibox.height then
data.h = data.items[1].wibox.height
end
-- Set methods
data.hide = hide
data.show = show
data.toggle = toggle
return data
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,150 @@
-------------------------------------------------------------------------
-- @author Sébastien Gross &lt;seb•ɱɩɲʋʃ•awesome•ɑƬ•chezwam•ɖɵʈ•org&gt
-- @copyright 2009 Sébastien Gross
-- @release v3.4.10
-------------------------------------------------------------------------
local mouse = mouse
local wibox = wibox
local screen = screen
local timer = timer
local a_placement = require("awful.placement")
local a_wibox = require("awful.wibox")
local beautiful = require("beautiful")
local setmetatable = setmetatable
--- Find the mouse pointer on the screen.
-- Mouse finder highlights the mouse cursor on the screen
-- <p>To enable this feature, a <code>awful.mouse.finder</code> object needs to
-- be bound to a key:<br/>
-- <code>mymousefinder = awful.mouse.finder()</code><br/>
-- Then bind the <code>find</code> function a key binding.
-- <p>Some configuration variable can be set in the theme:<br/>
-- The mouse_finder display duration<br/>
-- <code>theme.mouse_finder_timeout = 3</code><br/>
-- The animation speed<br/>
-- <code>theme.mouse_finder_animate_timeout = 0.05</code><br/>
-- The mouse_finder radius<br/>
-- <code>theme.mouse_finder_radius = 20</code><br/>
-- The growth factor<br/>
-- <code>theme.mouse_finder_factor = 2</code><br/>
-- The mouse_finder color<br/>
-- <code>theme.mouse_finder_color = "#ff0000"</code><br/>
-- </p>
module("awful.mouse.finder")
-- Mouse finder private data.
-- @name data
-- @field color Background color.
-- @field hide The hide() function.
-- @field show The show() function.
-- @field timer Timer to hide the mouse finder.
-- @field animate_timer Timer to animate the mouse finder.
-- @field wibox The mouse finder wibox show on the screen.
local data = setmetatable({}, { __mode = 'k' })
-- Place a mouse finder on the screen.
-- @param self A mouse finder object.
local function place(self)
a_placement.under_mouse(data[self].wibox)
a_placement.no_offscreen(data[self].wibox)
end
-- Animate a mouse finder.
-- @param self A mouse finder object.
local function animate(self)
local r = data[self].wibox:geometry().width
-- Check if the object should be grown or shrinked
-- the minimum radius is -data[self].factor because:
-- 1. factor is alway negative when shrinking
-- 2. geometry() does not hande negative values
if data[self].factor > 0 and r >= data[self].radius
or data[self].factor < 0 and r <= -data[self].factor then
data[self].factor = -data[self].factor
end
data[self].wibox:geometry({width = r + data[self].factor,
height = r + data[self].factor })
-- need -1 to the radius to draw a full circle
a_wibox.rounded_corners(data[self].wibox, (r + data[self].factor)/2 -1)
-- make sure the mouse finder follows the pointer. Uh!
place(self)
end
-- Show a mouse finder.
-- @param self The mouse finder to show.
local function show(self)
-- do nothing if the mouse finder is already shown
if data[self].wibox.visible then return end
if not data[self].timer.started then
-- make sure the mouse finder is on the same screen as the mouse
data[self].wibox.screen = mouse.screen
data[self].wibox:geometry({width = data[self].radius, height = data[self].radius })
a_wibox.rounded_corners(data[self].wibox, data[self].radius/2 -1)
data[self].timer:start()
data[self].animate_timer:start()
end
place(self)
data[self].wibox.visible = true
end
-- Hide a mouse finder.
-- @param self The mouse finder to hide.
local function hide(self)
-- do nothing if the mouse finder is already hidden
if not data[self].wibox.visible then return end
if data[self].timer.started then
data[self].timer:stop()
data[self].animate_timer:stop()
end
data[self].wibox.visible = false
end
-- Load Default values.
-- @param self A mouse finder object.
local function set_defaults(self)
data[self].wibox.border_width = 0
data[self].wibox.opacity = beautiful.mouse_finder_opacity or 1
data[self].wibox.bg = beautiful.mouse_finder_color or beautiful.bg_focus or "#ff0000"
data[self].timeout = beautiful.mouse_finder_timeout or 3
data[self].animate_timeout = beautiful.mouse_finder_animate_timeout or 0.05
data[self].radius = beautiful.mouse_finder_radius or 20
data[self].factor = beautiful.mouse_finder_factor or 2
end
--- Find the mouse on the screen
-- @param self A mouse finder object.
function find(self)
show(self)
end
--- Create a new mouse finder.
local function new()
local self = { }
-- private data
data[self] = {
wibox = wibox({ }),
show = function() show(self) end,
hide = function() hide(self) end,
animate = function() animate(self) end,
}
-- export functions
self.find = find
set_defaults(self)
-- setup the timer action only if needed
data[self].timer = timer { timeout = data[self].timeout }
data[self].animate_timer = timer { timeout = data[self].animate_timeout }
data[self].timer:add_signal("timeout", data[self].hide)
data[self].animate_timer:add_signal("timeout", data[self].animate)
data[self].wibox.ontop = true
data[self].wibox.visible = false
return self
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: ft=lua:et:sw=4:ts=4:sts=4:enc=utf-8:tw=78

View File

@ -0,0 +1,591 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local layout = require("awful.layout")
local tag = require("awful.tag")
local aclient = require("awful.client")
local widget = require("awful.widget")
local awibox = require("awful.wibox")
local util = require("awful.util")
local type = type
local math = math
local ipairs = ipairs
local capi =
{
root = root,
mouse = mouse,
screen = screen,
client = client,
mousegrabber = mousegrabber,
}
require("awful.mouse.finder")
--- Mouse module for awful
module("awful.mouse")
client = {}
wibox = {}
--- Get the client object under the pointer.
-- @return The client object under the pointer, if one can be found.
function client_under_pointer()
local obj = capi.mouse.object_under_pointer()
if type(obj) == "client" then
return obj
end
end
--- Get the wibox object under the pointer.
-- @return The wibox object under the pointer, if one can be found.
function wibox_under_pointer()
local obj = capi.mouse.object_under_pointer()
if type(obj) == "wibox" then
return obj
end
end
--- Get the widget under the pointer.
-- @return The widget object under the pointer, if it can be found.
function widget_under_pointer()
local obj, obj2 = capi.mouse.object_under_pointer()
if type(obj2) == "widget" then
return obj2
end
end
local function snap_outside(g, sg, snap)
if g.x < snap + sg.x + sg.width and g.x > sg.x + sg.width then
g.x = sg.x + sg.width
elseif g.x + g.width < sg.x and g.x + g.width > sg.x - snap then
g.x = sg.x - g.width
end
if g.y < snap + sg.y + sg.height and g.y > sg.y + sg.height then
g.y = sg.y + sg.height
elseif g.y + g.height < sg.y and g.y + g.height > sg.y - snap then
g.y = sg.y - g.height
end
return g
end
local function snap_inside(g, sg, snap)
local edgev = 'none'
local edgeh = 'none'
if math.abs(g.x) < snap + sg.x and g.x > sg.x then
edgev = 'left'
g.x = sg.x
elseif math.abs((sg.x + sg.width) - (g.x + g.width)) < snap then
edgev = 'right'
g.x = sg.x + sg.width - g.width
end
if math.abs(g.y) < snap + sg.y and g.y > sg.y then
edgeh = 'top'
g.y = sg.y
elseif math.abs((sg.y + sg.height) - (g.y + g.height)) < snap then
edgeh = 'bottom'
g.y = sg.y + sg.height - g.height
end
-- What is the dominant dimension?
if g.width > g.height then
return g, edgeh
else
return g, edgev
end
end
--- Snap a client to the closest client or screen edge.
-- @param c The client to snap.
-- @param snap The pixel to snap clients.
-- @param x The client x coordinate.
-- @param y The client y coordinate.
-- @param fixed_x True if the client isn't allowed to move in the x direction.
-- @param fixed_y True if the client isn't allowed to move in the y direction.
function client.snap(c, snap, x, y, fixed_x, fixed_y)
local snap = snap or 8
local c = c or client.focus
local cur_geom = c:geometry()
local geom = c:geometry()
geom.width = geom.width + (2 * c.border_width)
geom.height = geom.height + (2 * c.border_width)
local edge = "none"
local edge2 = "none"
geom.x = x or geom.x
geom.y = y or geom.y
geom, edge = snap_inside(geom, capi.screen[c.screen].geometry, snap)
geom = snap_inside(geom, capi.screen[c.screen].workarea, snap)
-- Allow certain windows to snap to the edge of the workarea.
-- Only allow docking to workarea for consistency/to avoid problems.
if aclient.dockable.get(c) then
local struts = c:struts()
struts['left'] = 0
struts['right'] = 0
struts['top'] = 0
struts['bottom'] = 0
if edge ~= "none" and aclient.floating.get(c) then
if edge == "left" or edge == "right" then
struts[edge] = cur_geom.width
elseif edge == "top" or edge == "bottom" then
struts[edge] = cur_geom.height
end
end
c:struts(struts)
end
geom.x = geom.x - (2 * c.border_width)
geom.y = geom.y - (2 * c.border_width)
for k, snapper in ipairs(aclient.visible(c.screen)) do
if snapper ~= c then
geom = snap_outside(geom, snapper:geometry(), snap)
end
end
-- It's easiest to undo changes afterwards if they're not allowed
if fixed_x then geom.x = cur_geom.x end
if fixed_y then geom.y = cur_geom.y end
geom.width = geom.width - (2 * c.border_width)
geom.height = geom.height - (2 * c.border_width)
geom.x = geom.x + (2 * c.border_width)
geom.y = geom.y + (2 * c.border_width)
return geom
end
--- Move a client.
-- @param c The client to move, or the focused one if nil.
-- @param snap The pixel to snap clients.
function client.move(c, snap)
local c = c or capi.client.focus
if not c
or c.fullscreen
or c.type == "desktop"
or c.type == "splash"
or c.type == "dock" then
return
end
c:raise()
local orig = c:geometry()
local m_c = capi.mouse.coords()
local dist_x = m_c.x - orig.x
local dist_y = m_c.y - orig.y
-- Only allow moving in the non-maximized directions
local fixed_x = c.maximized_horizontal
local fixed_y = c.maximized_vertical
capi.mousegrabber.run(function (mouse)
for k, v in ipairs(mouse.buttons) do
if v then
local lay = layout.get(c.screen)
if lay == layout.suit.floating or aclient.floating.get(c) then
local x = mouse.x - dist_x
local y = mouse.y - dist_y
c:geometry(client.snap(c, snap, x, y, fixed_x, fixed_y))
elseif lay ~= layout.suit.magnifier then
-- Only move the client to the mouse
-- screen if the target screen is not
-- floating.
-- Otherwise, we move if via geometry.
if layout.get(capi.mouse.screen) == layout.suit.floating then
local x = mouse.x - dist_x
local y = mouse.y - dist_y
c:geometry(client.snap(c, snap, x, y, fixed_x, fixed_y))
else
c.screen = capi.mouse.screen
end
if layout.get(c.screen) ~= layout.suit.floating then
local c_u_m = client_under_pointer()
if c_u_m and not aclient.floating.get(c_u_m) then
if c_u_m ~= c then
c:swap(c_u_m)
end
end
end
end
return true
end
end
return false
end, "fleur")
end
client.dragtotag = { }
--- Move a client to a tag by drag'n'dropping it over a taglist widget
-- @param c The client to move
function client.dragtotag.widget(c)
capi.mousegrabber.run(function (mouse)
local button_down = false
for _, v in ipairs(mouse.buttons) do
if v then button_down = true end
end
if not button_down then
local w = widget_under_pointer()
if w and widget.taglist.gettag(w) then
local t = widget.taglist.gettag(w)
if t.screen ~= c.screen then
aclient.movetoscreen(c, t.screen)
end
aclient.movetotag(t, c)
end
return false
end
return true
end, "fleur")
end
--- Move a client to a tag by dragging it onto the left / right side of the screen
-- @param c The client to move
function client.dragtotag.border(c)
capi.mousegrabber.run(function (mouse)
local button_down = false
for _, v in ipairs(mouse.buttons) do
if v then button_down = true end
end
local wa = capi.screen[c.screen].workarea
if mouse.x >= wa.x + wa.width then
capi.mouse.coords({ x = wa.x + wa.width - 1 })
elseif mouse.x <= wa.x then
capi.mouse.coords({ x = wa.x + 1 })
end
if not button_down then
local tags = capi.screen[c.screen]:tags()
local t = tag.selected()
local idx
for i, v in ipairs(tags) do
if v == t then
idx = i
end
end
if mouse.x > wa.x + wa.width - 10 then
local newtag = tags[util.cycle(#tags, idx + 1)]
aclient.movetotag(newtag, c)
tag.viewnext()
elseif mouse.x < wa.x + 10 then
local newtag = tags[util.cycle(#tags, idx - 1)]
aclient.movetotag(newtag, c)
tag.viewprev()
end
return false
end
return true
end, "fleur")
end
--- Move the wibox under the cursor
--@param w The wibox to move, or none to use that under the pointer
function wibox.move(w)
local w = w or wibox_under_pointer()
if not w then return end
local offset = {
x = w.x - capi.mouse.coords().x,
y = w.y - capi.mouse.coords().y
}
capi.mousegrabber.run(function (mouse)
local button_down = false
if awibox.get_position(w) == "floating" then
w.x = capi.mouse.coords().x + offset.x
w.y = capi.mouse.coords().y + offset.y
else
local wa = capi.screen[capi.mouse.screen].workarea
if capi.mouse.coords()["y"] > wa.y + wa.height - 10 then
awibox.set_position(w, "bottom", w.screen)
elseif capi.mouse.coords()["y"] < wa.y + 10 then
awibox.set_position(w, "top", w.screen)
elseif capi.mouse.coords()["x"] > wa.x + wa.width - 10 then
awibox.set_position(w, "right", w.screen)
elseif capi.mouse.coords()["x"] < wa.x + 10 then
awibox.set_position(w, "left", w.screen)
end
w.screen = capi.mouse.screen
end
for k, v in ipairs(mouse.buttons) do
if v then button_down = true end
end
if not button_down then
return false
end
return true
end, "fleur")
end
--- Get a client corner coordinates.
-- @param c The client to get corner from, focused one by default.
-- @param corner The corner to use: auto, top_left, top_right, bottom_left,
-- bottom_right. Default is auto, and auto find the nearest corner.
-- @return Actual used corner and x and y coordinates.
function client.corner(c, corner)
local c = c or capi.client.focus
if not c then return end
local g = c:geometry()
if not corner or corner == "auto" then
local m_c = capi.mouse.coords()
if math.abs(g.y - m_c.y) < math.abs(g.y + g.height - m_c.y) then
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
corner = "top_left"
else
corner = "top_right"
end
else
if math.abs(g.x - m_c.x) < math.abs(g.x + g.width - m_c.x) then
corner = "bottom_left"
else
corner = "bottom_right"
end
end
end
local x, y
if corner == "top_right" then
x = g.x + g.width
y = g.y
elseif corner == "top_left" then
x = g.x
y = g.y
elseif corner == "bottom_left" then
x = g.x
y = g.y + g.height
else
x = g.x + g.width
y = g.y + g.height
end
return corner, x, y
end
local function client_resize_magnifier(c, corner)
local corner, x, y = client.corner(c, corner)
capi.mouse.coords({ x = x, y = y })
local wa = capi.screen[c.screen].workarea
local center_x = wa.x + wa.width / 2
local center_y = wa.y + wa.height / 2
local maxdist_pow = (wa.width^2 + wa.height^2) / 4
capi.mousegrabber.run(function (mouse)
for k, v in ipairs(mouse.buttons) do
if v then
local dx = center_x - mouse.x
local dy = center_y - mouse.y
local dist = dx^2 + dy^2
-- New master width factor
local mwfact = dist / maxdist_pow
tag.setmwfact(math.min(math.max(0.01, mwfact), 0.99), tag.selected(c.screen))
return true
end
end
return false
end, corner .. "_corner")
end
local function client_resize_tiled(c, lay)
local wa = capi.screen[c.screen].workarea
local mwfact = tag.getmwfact()
local cursor
local g = c:geometry()
local offset = 0
local x,y
if lay == layout.suit.tile then
cursor = "cross"
if g.height+15 > wa.height then
offset = g.height * .5
cursor = "sb_h_double_arrow"
elseif not (g.y+g.height+15 > wa.y+wa.height) then
offset = g.height
end
capi.mouse.coords({ x = wa.x + wa.width * mwfact, y = g.y + offset })
elseif lay == layout.suit.tile.left then
cursor = "cross"
if g.height+15 >= wa.height then
offset = g.height * .5
cursor = "sb_h_double_arrow"
elseif not (g.y+g.height+15 > wa.y+wa.height) then
offset = g.height
end
capi.mouse.coords({ x = wa.x + wa.width * (1 - mwfact), y = g.y + offset })
elseif lay == layout.suit.tile.bottom then
cursor = "cross"
if g.width+15 >= wa.width then
offset = g.width * .5
cursor = "sb_v_double_arrow"
elseif not (g.x+g.width+15 > wa.x+wa.width) then
offset = g.width
end
capi.mouse.coords({ y = wa.y + wa.height * mwfact, x = g.x + offset})
else
cursor = "cross"
if g.width+15 >= wa.width then
offset = g.width * .5
cursor = "sb_v_double_arrow"
elseif not (g.x+g.width+15 > wa.x+wa.width) then
offset = g.width
end
capi.mouse.coords({ y = wa.y + wa.height * (1 - mwfact), x= g.x + offset })
end
capi.mousegrabber.run(function (mouse)
for k, v in ipairs(mouse.buttons) do
if v then
local fact_x = (mouse.x - wa.x) / wa.width
local fact_y = (mouse.y - wa.y) / wa.height
local mwfact
local g = c:geometry()
-- we have to make sure we're not on the last visible client where we have to use different settings.
local wfact
local wfact_x, wfact_y
if (g.y+g.height+15) > (wa.y+wa.height) then
wfact_y = (g.y + g.height - mouse.y) / wa.height
else
wfact_y = (mouse.y - g.y) / wa.height
end
if (g.x+g.width+15) > (wa.x+wa.width) then
wfact_x = (g.x + g.width - mouse.x) / wa.width
else
wfact_x = (mouse.x - g.x) / wa.width
end
if lay == layout.suit.tile then
mwfact = fact_x
wfact = wfact_y
elseif lay == layout.suit.tile.left then
mwfact = 1 - fact_x
wfact = wfact_y
elseif lay == layout.suit.tile.bottom then
mwfact = fact_y
wfact = wfact_x
else
mwfact = 1 - fact_y
wfact = wfact_x
end
tag.setmwfact(math.min(math.max(mwfact, 0.01), 0.99), tag.selected(c.screen))
aclient.setwfact(math.min(math.max(wfact,0.01), 0.99), c)
return true
end
end
return false
end, cursor)
end
local function client_resize_floating(c, corner, fixed_x, fixed_y)
local corner, x, y = client.corner(c, corner)
local g = c:geometry()
-- Warp mouse pointer
capi.mouse.coords({ x = x, y = y })
capi.mousegrabber.run(function (mouse)
for k, v in ipairs(mouse.buttons) do
if v then
-- Ignore screen changes
if not aclient.floating.get(c)
and capi.mouse.screen ~= c.screen then
return true
end
local ng
if corner == "bottom_right" then
ng = { width = mouse.x - g.x,
height = mouse.y - g.y }
elseif corner == "bottom_left" then
ng = { x = mouse.x,
width = (g.x + g.width) - mouse.x,
height = mouse.y - g.y }
elseif corner == "top_left" then
ng = { x = mouse.x,
width = (g.x + g.width) - mouse.x,
y = mouse.y,
height = (g.y + g.height) - mouse.y }
else
ng = { width = mouse.x - g.x,
y = mouse.y,
height = (g.y + g.height) - mouse.y }
end
if ng.width <= 0 then ng.width = nil end
if ng.height <= 0 then ng.height = nil end
if fixed_x then ng.width = g.width ng.x = g.x end
if fixed_y then ng.height = g.height ng.y = g.y end
c:geometry(ng)
-- Get real geometry that has been applied
-- in case we honor size hints
-- XXX: This should be rewritten when size
-- hints are available from Lua.
local rg = c:geometry()
if corner == "bottom_right" then
ng = {}
elseif corner == "bottom_left" then
ng = { x = (g.x + g.width) - rg.width }
elseif corner == "top_left" then
ng = { x = (g.x + g.width) - rg.width,
y = (g.y + g.height) - rg.height }
else
ng = { y = (g.y + g.height) - rg.height }
end
c:geometry({ x = ng.x, y = ng.y })
return true
end
end
return false
end, corner .. "_corner")
end
--- Resize a client.
-- @param c The client to resize, or the focused one by default.
-- @param corner The corner to grab on resize. Auto detected by default.
function client.resize(c, corner)
local c = c or capi.client.focus
if not c then return end
if c.fullscreen
or c.type == "desktop"
or c.type == "splash"
or c.type == "dock" then
return
end
-- Do not allow maximized clients to be resized by mouse
local fixed_x = c.maximized_horizontal
local fixed_y = c.maximized_vertical
local lay = layout.get(c.screen)
if lay == layout.suit.floating or aclient.floating.get(c) then
return client_resize_floating(c, corner, fixed_x, fixed_y)
elseif lay == layout.suit.tile
or lay == layout.suit.tile.left
or lay == layout.suit.tile.top
or lay == layout.suit.tile.bottom
then
return client_resize_tiled(c, lay)
elseif lay == layout.suit.magnifier then
return client_resize_magnifier(c, corner)
end
end
-- Set the cursor at startup
capi.root.cursor("left_ptr")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,238 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local pairs = pairs
local math = math
local table = table
local capi =
{
screen = screen,
mouse = mouse,
client = client
}
local client = require("awful.client")
local layout = require("awful.layout")
--- Places client according to special criteria.
module("awful.placement")
-- Check if an area intersect another area.
-- @param a The area.
-- @param b The other area.
-- @return True if they intersect, false otherwise.
local function area_intersect_area(a, b)
return (b.x < a.x + a.width
and b.x + b.width > a.x
and b.y < a.y + a.height
and b.y + b.height > a.y)
end
-- Get the intersect area between a and b.
-- @param a The area.
-- @param b The other area.
-- @return The intersect area.
local function area_intersect_area_get(a, b)
local g = {}
g.x = math.max(a.x, b.x)
g.y = math.max(a.y, b.y)
g.width = math.min(a.x + a.width, b.x + b.width) - g.x
g.height = math.min(a.y + a.height, b.y + b.height) - g.y
return g
end
-- Remove an area from a list, splitting the space between several area that
-- can overlap.
-- @param areas Table of areas.
-- @param elem Area to remove.
-- @return The new area list.
local function area_remove(areas, elem)
for i = #areas, 1, -1 do
-- Check if the 'elem' intersect
if area_intersect_area(areas[i], elem) then
-- It does? remove it
local r = table.remove(areas, i)
local inter = area_intersect_area_get(r, elem)
if inter.x > r.x then
table.insert(areas, {
x = r.x,
y = r.y,
width = inter.x - r.x,
height = r.height
})
end
if inter.y > r.y then
table.insert(areas, {
x = r.x,
y = r.y,
width = r.width,
height = inter.y - r.y
})
end
if inter.x + inter.width < r.x + r.width then
table.insert(areas, {
x = inter.x + inter.width,
y = r.y,
width = (r.x + r.width) - (inter.x + inter.width),
height = r.height
})
end
if inter.y + inter.height < r.y + r.height then
table.insert(areas, {
x = r.x,
y = inter.y + inter.height,
width = r.width,
height = (r.y + r.height) - (inter.y + inter.height)
})
end
end
end
return areas
end
--- Place the client so no part of it will be outside the screen.
-- @param c The client.
-- @return The new client geometry.
function no_offscreen(c)
local c = c or capi.client.focus
local geometry = c:geometry()
local border = c.border_width
local screen_geometry = capi.screen[c.screen].workarea
if geometry.x + geometry.width + 2*border > screen_geometry.x + screen_geometry.width then
geometry.x = screen_geometry.x + screen_geometry.width - geometry.width
elseif geometry.x < screen_geometry.x then
geometry.x = screen_geometry.x
end
if geometry.y + geometry.height + border > screen_geometry.y + screen_geometry.height then
geometry.y = screen_geometry.y + screen_geometry.height - geometry.height
elseif geometry.y < screen_geometry.y then
geometry.y = screen_geometry.y
end
c:geometry(geometry)
end
--- Place the client where there's place available with minimum overlap.
-- @param c The client.
function no_overlap(c)
local cls = client.visible(c.screen)
local curlay = layout.get()
local areas = { capi.screen[c.screen].workarea }
local geometry = c:geometry()
for i, cl in pairs(cls) do
if cl ~= c and cl.type ~= "desktop" and (client.floating.get(cl) or curlay == layout.suit.floating) then
areas = area_remove(areas, cl:geometry())
end
end
-- Look for available space
local found = false
local new = { x = geometry.x, y = geometry.y, width = 0, height = 0 }
for i, r in ipairs(areas) do
if r.width >= geometry.width
and r.height >= geometry.height
and r.width * r.height > new.width * new.height then
found = true
new = r
-- Check if the client's current position is available
-- and prefer that one (why move it around pointlessly?)
if geometry.x >= r.x
and geometry.y >= r.y
and geometry.x + geometry.width <= r.x + r.width
and geometry.y + geometry.height <= r.y + r.height then
new.x = geometry.x
new.y = geometry.y
end
end
end
-- We did not find an area with enough space for our size:
-- just take the biggest available one and go in
if not found then
for i, r in ipairs(areas) do
if r.width * r.height > new.width * new.height then
new = r
end
end
end
-- Restore height and width
new.width = geometry.width
new.height = geometry.height
return c:geometry(new)
end
--- Place the client under the mouse.
-- @param c The client.
-- @return The new client geometry.
function under_mouse(c)
local c = c or capi.client.focus
local c_geometry = c:geometry()
local m_coords = capi.mouse.coords()
return c:geometry({ x = m_coords.x - c_geometry.width / 2,
y = m_coords.y - c_geometry.height / 2 })
end
--- Place the client centered with respect to a parent or the clients screen.
-- @param c The client.
-- @param p The parent (optional, nil for screen centering).
-- @return The new client geometry.
function centered(c, p)
local c = c or capi.client.focus
local c_geometry = c:geometry()
local s_geometry
if p then
s_geometry = p:geometry()
else
s_geometry = capi.screen[c.screen].geometry
end
return c:geometry({ x = s_geometry.x + (s_geometry.width - c_geometry.width) / 2,
y = s_geometry.y + (s_geometry.height - c_geometry.height) / 2 })
end
--- Place the client centered on the horizontal axis with respect to a parent or the clients screen.
-- @param c The client.
-- @param p The parent (optional, nil for screen centering).
-- @return The new client geometry.
function center_horizontal(c, p)
local c = c or capi.client.focus
local c_geometry = c:geometry()
local s_geometry
if p then
s_geometry = p:geometry()
else
s_geometry = capi.screen[c.screen].geometry
end
return c:geometry({ x = s_geometry.x + (s_geometry.width - c_geometry.width) / 2 })
end
--- Place the client centered on the vertical axis with respect to a parent or the clients screen.
-- @param c The client.
-- @param p The parent (optional, nil for screen centering).
-- @return The new client geometry.
function center_vertical(c, p)
local c = c or capi.client.focus
local c_geometry = c:geometry()
local s_geometry
if p then
s_geometry = p:geometry()
else
s_geometry = capi.screen[c.screen].geometry
end
return c:geometry({ y = s_geometry.y + (s_geometry.height - c_geometry.height) / 2 })
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,381 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local assert = assert
local io = io
local table = table
local math = math
local ipairs = ipairs
local pcall = pcall
local capi =
{
keygrabber = keygrabber,
selection = selection
}
local util = require("awful.util")
local beautiful = require("beautiful")
--- Prompt module for awful
module("awful.prompt")
--- Private data
local data = {}
data.history = {}
-- Load history file in history table
-- @param id The data.history identifier which is the path to the filename
-- @param max Optional parameter, the maximum number of entries in file
local function history_check_load(id, max)
if id and id ~= ""
and not data.history[id] then
data.history[id] = { max = 50, table = {} }
if max then
data.history[id].max = max
end
local f = io.open(id, "r")
-- Read history file
if f then
for line in f:lines() do
table.insert(data.history[id].table, line)
if #data.history[id].table >= data.history[id].max then
break
end
end
f:close()
end
end
end
-- Save history table in history file
-- @param id The data.history identifier
local function history_save(id)
if data.history[id] then
local f = io.open(id, "w")
if not f then
local i = 0
for d in id:gmatch(".-/") do
i = i + #d
end
util.mkdir(id:sub(1, i - 1))
f = assert(io.open(id, "w"))
end
for i = 1, math.min(#data.history[id].table, data.history[id].max) do
f:write(data.history[id].table[i] .. "\n")
end
f:close()
end
end
-- Return the number of items in history table regarding the id
-- @param id The data.history identifier
-- @return the number of items in history table, -1 if history is disabled
local function history_items(id)
if data.history[id] then
return #data.history[id].table
else
return -1
end
end
-- Add an entry to the history file
-- @param id The data.history identifier
-- @param command The command to add
local function history_add(id, command)
if data.history[id] then
if command ~= ""
and command ~= data.history[id].table[#data.history[id].table] then
table.insert(data.history[id].table, command)
-- Do not exceed our max_cmd
if #data.history[id].table > data.history[id].max then
table.remove(data.history[id].table, 1)
end
history_save(id)
end
end
end
-- Draw the prompt text with a cursor.
-- @param args The table of arguments.
-- @param text The text.
-- @param font The font.
-- @param prompt The text prefix.
-- @param text_color The text color.
-- @param cursor_color The cursor color.
-- @param cursor_pos The cursor position.
-- @param cursor_ul The cursor underline style.
-- @param selectall If true cursor is rendered on the entire text.
local function prompt_text_with_cursor(args)
local char, spacer, text_start, text_end, ret
local text = args.text or ""
local prompt = args.prompt or ""
local underline = args.cursor_ul or "none"
if args.selectall then
if #text == 0 then char = " " else char = util.escape(text) end
spacer = " "
text_start = ""
text_end = ""
elseif #text < args.cursor_pos then
char = " "
spacer = ""
text_start = util.escape(text)
text_end = ""
else
char = util.escape(text:sub(args.cursor_pos, args.cursor_pos))
spacer = " "
text_start = util.escape(text:sub(1, args.cursor_pos - 1))
text_end = util.escape(text:sub(args.cursor_pos + 1))
end
ret = prompt .. text_start .. "<span background=\"" .. util.color_strip_alpha(args.cursor_color) .. "\" foreground=\"" .. util.color_strip_alpha(args.text_color) .. "\" underline=\"" .. underline .. "\">" .. char .. "</span>" .. text_end .. spacer
if args.font then ret = "<span font_desc='" .. args.font .. "'>" .. ret .. "</span>" end
return ret
end
--- Run a prompt in a box.
-- @param args A table with optional arguments: fg_cursor, bg_cursor, ul_cursor, prompt, text, selectall, font, autoexec.
-- @param textbox The textbox to use for the prompt.
-- @param exe_callback The callback function to call with command as argument when finished.
-- @param completion_callback The callback function to call to get completion.
-- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
-- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
-- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
function run(args, textbox, exe_callback, completion_callback, history_path, history_max, done_callback)
local theme = beautiful.get()
if not args then args = {} end
local command = args.text or ""
local command_before_comp
local cur_pos_before_comp
local prettyprompt = args.prompt or ""
local inv_col = args.fg_cursor or theme.fg_focus or "black"
local cur_col = args.bg_cursor or theme.bg_focus or "white"
local cur_ul = args.ul_cursor
local text = args.text or ""
local font = args.font or theme.font
local selectall = args.selectall
history_check_load(history_path, history_max)
local history_index = history_items(history_path) + 1
-- The cursor position
local cur_pos = (selectall and 1) or text:wlen() + 1
-- The completion element to use on completion request.
local ncomp = 1
if not textbox or not exe_callback then
return
end
textbox.text = prompt_text_with_cursor{
text = text, text_color = inv_col, cursor_color = cur_col,
cursor_pos = cur_pos, cursor_ul = cur_ul, selectall = selectall,
font = font, prompt = prettyprompt }
local exec = function()
textbox.text = ""
history_add(history_path, command)
capi.keygrabber.stop()
exe_callback(command)
if done_callback then done_callback() end
end
capi.keygrabber.run(
function (modifiers, key, event)
if event ~= "press" then return true end
-- Convert index array to hash table
local mod = {}
for k, v in ipairs(modifiers) do mod[v] = true end
-- Get out cases
if (mod.Control and (key == "c" or key == "g"))
or (not mod.Control and key == "Escape") then
textbox.text = ""
if done_callback then done_callback() end
return false
elseif (mod.Control and (key == "j" or key == "m"))
or (not mod.Control and key == "Return")
or (not mod.Control and key == "KP_Enter") then
exec()
-- We already unregistered ourselves so we don't want to return
-- true, otherwise we may unregister someone else.
return true
end
-- Control cases
if mod.Control then
selectall = nil
if key == "a" then
cur_pos = 1
elseif key == "b" then
if cur_pos > 1 then
cur_pos = cur_pos - 1
end
elseif key == "d" then
if cur_pos <= #command then
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
end
elseif key == "e" then
cur_pos = #command + 1
elseif key == "f" then
if cur_pos <= #command then
cur_pos = cur_pos + 1
end
elseif key == "h" then
if cur_pos > 1 then
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
end
elseif key == "k" then
command = command:sub(1, cur_pos - 1)
elseif key == "u" then
command = command:sub(cur_pos, #command)
cur_pos = 1
elseif key == "w" or key == "BackSpace" then
local wstart = 1
local wend = 1
local cword_start = 1
local cword_end = 1
while wend < cur_pos do
wend = command:find("[{[(,.:;_-+=@/ ]", wstart)
if not wend then wend = #command + 1 end
if cur_pos >= wstart and cur_pos <= wend + 1 then
cword_start = wstart
cword_end = cur_pos - 1
break
end
wstart = wend + 1
end
command = command:sub(1, cword_start - 1) .. command:sub(cword_end + 1)
cur_pos = cword_start
end
else
if completion_callback then
if key == "Tab" or key == "ISO_Left_Tab" then
if key == "ISO_Left_Tab" then
if ncomp == 1 then return true end
if ncomp == 2 then
command = command_before_comp
textbox.text = prompt_text_with_cursor{
text = command_before_comp, text_color = inv_col, cursor_color = cur_col,
cursor_pos = cur_pos, cursor_ul = cur_ul, selectall = selectall,
font = font, prompt = prettyprompt }
return true
end
ncomp = ncomp - 2
elseif ncomp == 1 then
command_before_comp = command
cur_pos_before_comp = cur_pos
end
local matches
command, cur_pos, matches = completion_callback(command_before_comp, cur_pos_before_comp, ncomp)
ncomp = ncomp + 1
key = ""
-- execute if only one match found and autoexec flag set
if matches and #matches == 1 and args.autoexec then
exec()
return true
end
else
ncomp = 1
end
end
-- Typin cases
if mod.Shift and key == "Insert" then
local selection = capi.selection()
if selection then
-- Remove \n
local n = selection:find("\n")
if n then
selection = selection:sub(1, n - 1)
end
command = command:sub(1, cur_pos - 1) .. selection .. command:sub(cur_pos)
cur_pos = cur_pos + #selection
end
elseif key == "Home" then
cur_pos = 1
elseif key == "End" then
cur_pos = #command + 1
elseif key == "BackSpace" then
if cur_pos > 1 then
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
end
elseif key == "Delete" then
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
elseif key == "Left" then
cur_pos = cur_pos - 1
elseif key == "Right" then
cur_pos = cur_pos + 1
elseif key == "Up" then
if history_index > 1 then
history_index = history_index - 1
command = data.history[history_path].table[history_index]
cur_pos = #command + 2
end
elseif key == "Down" then
if history_index < history_items(history_path) then
history_index = history_index + 1
command = data.history[history_path].table[history_index]
cur_pos = #command + 2
elseif history_index == history_items(history_path) then
history_index = history_index + 1
command = ""
cur_pos = 1
end
else
-- wlen() is UTF-8 aware but #key is not,
-- so check that we have one UTF-8 char but advance the cursor of # position
if key:wlen() == 1 then
if selectall then command = "" end
command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos)
cur_pos = cur_pos + #key
end
end
if cur_pos < 1 then
cur_pos = 1
elseif cur_pos > #command + 1 then
cur_pos = #command + 1
end
selectall = nil
end
-- Update textbox
local function update()
textbox.text = prompt_text_with_cursor{
text = command, text_color = inv_col, cursor_color = cur_col,
cursor_pos = cur_pos, cursor_ul = cur_ul, selectall = selectall,
font = font, prompt = prettyprompt }
end
local success = pcall(update)
while not success do
-- TODO UGLY HACK TODO
-- Setting the text failed. Most likely reason is that the user
-- entered a multibyte character and pressed backspace which only
-- removed the last byte. Let's remove another byte.
if cur_pos <= 1 then
-- No text left?!
break
end
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
success = pcall(update)
end
return true
end)
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,48 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
require("awful.dbus")
local loadstring = loadstring
local tostring = tostring
local ipairs = ipairs
local table = table
local dbus = dbus
local unpack = unpack
local type = type
--- Remote control module allowing usage of awesome-client.
module("awful.remote")
if dbus then
dbus.add_signal("org.naquadah.awesome.awful.Remote", function(data, code)
if data.member == "Eval" then
local f, e = loadstring(code)
if f then
results = { f() }
retvals = {}
for _, v in ipairs(results) do
local t = type(v)
if t == "boolean" then
table.insert(retvals, "b")
table.insert(retvals, v)
elseif t == "number" then
table.insert(retvals, "d")
table.insert(retvals, v)
else
table.insert(retvals, "s")
table.insert(retvals, tostring(v))
end
end
return unpack(retvals)
elseif e then
return "s", e
end
end
end)
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,203 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local client = client
local table = table
local type = type
local ipairs = ipairs
local pairs = pairs
local aclient = require("awful.client")
local atag = require("awful.tag")
--- Apply rules to clients at startup.
module("awful.rules")
--- This is the global rules table.
-- <p>You should fill this table with your rule and properties to apply.
-- For example, if you want to set xterm maximized at startup, you can add:
-- <br/>
-- <code>
-- { rule = { class = "xterm" },
-- properties = { maximized_vertical = true, maximized_horizontal = true } }
-- </code>
-- </p>
-- <p>If you want to set mplayer floating at startup, you can add:
-- <br/>
-- <code>
-- { rule = { name = "MPlayer" },
-- properties = { floating = true } }
-- </code>
-- </p>
-- <p>If you want to put Firefox on a specific tag at startup, you
-- can add:
-- <br/>
-- <code>
-- { rule = { instance = "firefox" },
-- properties = { tag = mytagobject } }
-- </code>
-- </p>
-- <p>If you want to put Emacs on a specific tag at startup, and
-- immediately switch to that tag you can add:
-- <br/>
-- <code>
-- { rule = { class = "Emacs" },
-- properties = { tag = mytagobject, switchtotag = true } }
-- </code>
-- </p>
-- <p>If you want to apply a custom callback to execute when a rule matched, you
-- can add:
-- <br/>
-- <code>
-- { rule = { class = "dosbox" },
-- callback = awful.placement.centered }
-- </code>
-- </p>
-- <p>Note that all "rule" entries need to match. If any of the entry does not
-- match, the rule won't be applied.</p>
-- <p>If a client matches multiple rules, their applied in the order they are
-- put in this global rules table. If the value of a rule is a string, then the
-- match function is used to determine if the client matches the rule.</p>
--
-- <p> To match multiple clients to a rule one need to use slightly different
-- syntax:
-- <br/>
-- <code>
-- { rule_any = { class = { "MPlayer", "Nitrogen" }, instance = { "xterm" } },
-- properties = { floating = true } }
-- </code>
-- </p>
--
-- <p> To match multiple clients with an exception one can couple 'except' or
-- 'except_any' with the rules:
-- <br/>
-- <code>
-- { rule = { class = "Firefox" },
-- except = { instance = "Navigator" },
-- properties = {floating = true},
-- },
-- </code>
-- <br/>
-- <code>
-- { rule_any = { class = { "Pidgin", "Xchat" } },
-- except_any = { role = { "conversation" } },
-- properties = { tag = tags[1][1] }
-- }
-- <br/>
-- <code>
-- { rule = {},
-- except_any = { class = { "Firefox", "Vim" } },
-- properties = { floating = true }
-- }
-- </code>
-- </p>
--
-- @class table
-- @name rules
rules = {}
--- Check if a client match a rule.
-- @param c The client.
-- @param rule The rule to check.
-- @return True if it matches, false otherwise.
function match(c, rule)
if not rule then return false end
for field, value in pairs(rule) do
if c[field] then
if type(c[field]) == "string" then
if not c[field]:match(value) and c[field] ~= value then
return false
end
elseif c[field] ~= value then
return false
end
else
return false
end
end
return true
end
--- Check if a client match a rule. Multiple clients can be matched
-- @param c The client.
-- @param rules The rule to check.
-- @return True if at least one rule is matched, false otherwise.
function match_any(c, rule)
if not rule then return false end
for field, values in pairs(rule) do
if c[field] then
for _, value in ipairs(values) do
if c[field] == value then
return true
elseif type(c[field]) == "string" and c[field]:match(value) then
return true
end
end
end
end
return false
end
--- Apply rules to a client.
-- @param c The client.
function apply(c)
local props = {}
local callbacks = {}
for _, entry in ipairs(rules) do
if (match(c, entry.rule) or match_any(c, entry.rule_any)) and
(not match(c, entry.except) and not match_any(c, entry.except_any)) then
if entry.properties then
for property, value in pairs(entry.properties) do
props[property] = value
end
end
if entry.callback then
table.insert(callbacks, entry.callback)
end
end
end
for property, value in pairs(props) do
if property == "floating" then
aclient.floating.set(c, value)
elseif property == "tag" then
c:tags({ value })
c.screen = value.screen
elseif property == "switchtotag" and value and props.tag then
atag.viewonly(props.tag)
elseif property == "height" or property == "width" or
property == "x" or property == "y" then
local geo = c:geometry();
geo[property] = value
c:geometry(geo);
elseif type(c[property]) == "function" then
c[property](c, value)
else
c[property] = value
end
end
-- If untagged, stick the client on the current one.
if #c:tags() == 0 then
atag.withcurrent(c)
end
-- Apply all callbacks from matched rules.
for i, callback in pairs(callbacks) do
callback(c)
end
-- Do this at last so we do not erase things done by the focus
-- signal.
if props.focus then
client.focus = c
end
end
client.add_signal("manage", apply)
client.remove_signal("manage", atag.withcurrent)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,53 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local capi =
{
mouse = mouse,
screen = screen,
client = client
}
local util = require("awful.util")
local client = require("awful.client")
--- Screen module for awful
module("awful.screen")
local data = {}
data.padding = {}
--- Give the focus to a screen, and move pointer.
-- @param screen Screen number.
function focus(screen)
if screen > capi.screen.count() then screen = capi.mouse.screen end
local c = client.focus.history.get(screen, 0)
if c then capi.client.focus = c end
-- Move the mouse on the screen
capi.mouse.screen = screen
end
--- Give the focus to a screen, and move pointer, but relative to the current
-- focused screen.
-- @param i Value to add to the current focused screen index. 1 will focus next
-- screen, -1 would focus the previous one.
function focus_relative(i)
return focus(util.cycle(capi.screen.count(), capi.mouse.screen + i))
end
--- Get or set the screen padding.
-- @param screen The screen object to change the padding on
-- @param padding The padding, an table with 'top', 'left', 'right' and/or
-- 'bottom'. Can be nil if you only want to retrieve padding
function padding(screen, padding)
if padding then
data.padding[screen] = padding
screen:emit_signal("padding")
end
return data.padding[screen]
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,54 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local ipairs = ipairs
local table = table
local capi =
{
awesome = awesome,
root = root
}
--- Startup notification module for awful
module("awful.startup_notification")
local app_starting = {}
cursor_waiting = "watch"
local function update_cursor()
if #app_starting > 0 then
capi.root.cursor(cursor_waiting)
else
capi.root.cursor("left_ptr")
end
end
local function unregister_event(event_id)
for k, v in ipairs(app_starting) do
if v == event_id then
table.remove(app_starting, k)
update_cursor()
break
end
end
end
local function register_event(event_id)
table.insert(app_starting, event_id)
update_cursor()
end
local function unregister_hook(event) unregister_event(event.id) end
local function register_hook(event) register_event(event.id) end
capi.awesome.add_signal("spawn::initiated", register_hook)
capi.awesome.add_signal("spawn::canceled", unregister_hook)
capi.awesome.add_signal("spawn::completed", unregister_hook)
capi.awesome.add_signal("spawn::timeout", unregister_hook)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,527 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local util = require("awful.util")
local tostring = tostring
local pairs = pairs
local ipairs = ipairs
local table = table
local setmetatable = setmetatable
local capi =
{
tag = tag,
screen = screen,
mouse = mouse,
client = client
}
--- Useful functions for tag manipulation.
module("awful.tag")
-- Private data
local data = {}
data.history = {}
data.tags = setmetatable({}, { __mode = 'k' })
-- History functions
history = {}
history.limit = 20
--- Move a tag to an absolute position in the screen[]:tags() table.
-- @param new_index Integer absolute position in the table to insert.
function move(new_index, target_tag)
local target_tag = target_tag or selected()
local scr = target_tag.screen
local tmp_tags = capi.screen[scr]:tags()
if (not new_index) or (new_index < 1) or (new_index > #tmp_tags) then
return
end
for i, t in ipairs(tmp_tags) do
if t == target_tag then
table.remove(tmp_tags, i)
break
end
end
table.insert(tmp_tags, new_index, target_tag)
capi.screen[scr]:tags(tmp_tags)
end
--- Add a tag.
-- @param name The tag name, a string
-- @param props The tags properties, a table
-- @return The created tag
function add(name, props)
local properties = props or {}
local newtag = capi.tag{name = name}
newtag.screen = properties.screen or capi.mouse.screen
for k, v in pairs(properties) do
setproperty(newtag, k, v)
end
return newtag
end
--- Create a set of tags and attach it to a screen.
-- @param names The tag name, in a table
-- @param screen The tag screen, or 1 if not set.
-- @param layout The layout or layout table to set for this tags by default.
-- @return A table with all created tags.
function new(names, screen, layout)
local screen = screen or 1
local tags = {}
for id, name in ipairs(names) do
table.insert(tags, id, add(name, {screen = screen,
layout = (layout and layout[id]) or
layout}))
-- Select the first tag.
if id == 1 then
tags[id].selected = true
end
end
return tags
end
--- Find a suitable fallback tag.
-- @param screen The screen number to look for a tag on. [mouse.screen]
-- @param target A table of tags we consider unacceptable. [selectedlist(scr)]
function find_fallback(screen, invalids)
local scr = screen or capi.mouse.screen
local t = invalids or selectedlist(scr)
for _, v in pairs(capi.screen[scr]:tags()) do
if not util.table.hasitem(t, v) then return v end
end
end
--- Delete a tag.
-- @param target_tag Optional tag object to delete. [selected()]
-- @param fallback_tag Tag to assign stickied tags to. [~selected()]
-- @return Returns true if the tag is successfully deleted, nil otherwise.
-- If there are no clients exclusively on this tag then delete it. Any
-- stickied clients are assigned to the optional 'fallback_tag'.
-- If after deleting the tag there is no selected tag, try and restore from
-- history or select the first tag on the screen.
function delete(target_tag, fallback_tag)
-- abort if no tag is passed or currently selected
local target_tag = target_tag or selected()
if target_tag == nil then return end
local ntags = #capi.screen[target_tag.screen]:tags()
local target_scr = target_tag.screen
-- We can't use the target tag as a fallback.
local fallback_tag = fallback_tag
if fallback_tag == target_tag then return end
-- No fallback_tag provided, try and get one.
if fallback_tag == nil then
fallback_tag = find_fallback(target_scr, {target_tag})
end
-- Abort if we would have un-tagged clients.
local clients = target_tag:clients()
if ( #clients > 0 and ntags <= 1 ) or fallback_tag == nil then return end
-- Move the clients we can off of this tag.
for _, c in pairs(clients) do
-- If a client has only this tag, or stickied clients with
-- nowhere to go, abort.
if (not c.sticky and #c:tags() == 1) or
(c.sticky and fallback_tag == nil) then
return
else
c:tags({fallback_tag})
end
end
-- delete the tag
target_tag.screen = nil
-- If no tags are visible, try and view one.
if selected(target_scr) == nil and ntags > 0 then
history.restore()
if selected(target_scr) == nil then
capi.screen[target_scr]:tags()[1].selected = true
end
end
return true
end
--- Update the tag history.
-- @param obj Screen object.
function history.update(obj)
local s = obj.index
local curtags = selectedlist(s)
-- create history table
if not data.history[s] then
data.history[s] = {}
else
if data.history[s].current then
-- Check that the list is not identical
local identical = true
for idx, tag in ipairs(data.history[s].current) do
if curtags[idx] ~= tag then
identical = false
break
end
end
-- Do not update history the table are identical
if identical then return end
end
-- Limit history
if #data.history[s] >= history.limit then
for i = history.limit, #data.history[s] do
data.history[s][i] = nil
end
end
end
-- store previously selected tags in the history table
table.insert(data.history[s], 1, data.history[s].current)
data.history[s].previous = data.history[s][1]
-- store currently selected tags
data.history[s].current = setmetatable(curtags, { __mode = 'v' })
end
--- Revert tag history.
-- @param screen The screen number.
-- @param idx Index in history. Defaults to "previous" which is a special index
-- toggling between last two selected sets of tags. Number (eg 1) will go back
-- to the given index in history.
function history.restore(screen, idx)
local s = screen or capi.mouse.screen
local i = idx or "previous"
local sel = selectedlist(s)
-- do nothing if history empty
if not data.history[s] or not data.history[s][i] then return end
-- if all tags been deleted, try next entry
if #data.history[s][i] == 0 then
if i == "previous" then i = 0 end
history.restore(s, i + 1)
return
end
-- deselect all tags
viewnone(s)
-- select tags from the history entry
for _, t in ipairs(data.history[s][i]) do
t.selected = true
end
-- update currently selected tags table
data.history[s].current = data.history[s][i]
-- store previously selected tags
data.history[s].previous = setmetatable(sel, { __mode = 'v' })
-- remove the reverted history entry
if i ~= "previous" then table.remove(data.history[s], i) end
end
--- Return a table with all visible tags
-- @param s Screen number.
-- @return A table with all selected tags.
function selectedlist(s)
local screen = s or capi.mouse.screen
local tags = capi.screen[screen]:tags()
local vtags = {}
for i, t in pairs(tags) do
if t.selected then
vtags[#vtags + 1] = t
end
end
return vtags
end
--- Return only the first visible tag.
-- @param s Screen number.
function selected(s)
return selectedlist(s)[1]
end
--- Set master width factor.
-- @param mwfact Master width factor.
function setmwfact(mwfact, t)
local t = t or selected()
if mwfact >= 0 and mwfact <= 1 then
setproperty(t, "mwfact", mwfact)
end
end
--- Increase master width factor.
-- @param add Value to add to master width factor.
function incmwfact(add, t)
setmwfact(getmwfact(t) + add)
end
--- Get master width factor.
-- @param t Optional tag.
function getmwfact(t)
local t = t or selected()
return getproperty(t, "mwfact") or 0.5
end
--- Set the number of master windows.
-- @param nmaster The number of master windows.
-- @param t Optional tag.
function setnmaster(nmaster, t)
local t = t or selected()
if nmaster >= 0 then
setproperty(t, "nmaster", nmaster)
end
end
--- Get the number of master windows.
-- @param t Optional tag.
function getnmaster(t)
local t = t or selected()
return getproperty(t, "nmaster") or 1
end
--- Increase the number of master windows.
-- @param add Value to add to number of master windows.
function incnmaster(add, t)
setnmaster(getnmaster(t) + add)
end
--- Set the tag icon
-- @param icon the icon to set, either path or image object
-- @param tag the tag
function seticon(icon, tag)
local tag = tag or selected()
setproperty(tag, "icon", icon)
end
--- Get the tag icon
-- @param t the tag
function geticon(tag)
local tag = tag or selected()
return getproperty(tag, "icon")
end
--- Set number of column windows.
-- @param ncol The number of column.
function setncol(ncol, t)
local t = t or selected()
if ncol >= 1 then
setproperty(t, "ncol", ncol)
end
end
--- Get number of column windows.
-- @param t Optional tag.
function getncol(t)
local t = t or selected()
return getproperty(t, "ncol") or 1
end
--- Increase number of column windows.
-- @param add Value to add to number of column windows.
function incncol(add, t)
setncol(getncol(t) + add)
end
--- View no tag.
-- @param Optional screen number.
function viewnone(screen)
local tags = capi.screen[screen or capi.mouse.screen]:tags()
for i, t in pairs(tags) do
t.selected = false
end
end
--- View a tag by its taglist index.
-- @param i The relative index to see.
-- @param screen Optional screen number.
function viewidx(i, screen)
local screen = screen and screen.index or capi.mouse.screen
local tags = capi.screen[screen]:tags()
local showntags = {}
for k, t in ipairs(tags) do
if not getproperty(t, "hide") then
table.insert(showntags, t)
end
end
local sel = selected(screen)
viewnone(screen)
for k, t in ipairs(showntags) do
if t == sel then
showntags[util.cycle(#showntags, k + i)].selected = true
end
end
capi.screen[screen]:emit_signal("tag::history::update")
end
--- Get a tag's index in the screen[]:tags() table.
-- @param query_tag The tag object to find. [selected()]
-- @return The index of the tag, nil if the tag is not found.
function getidx(query_tag)
local query_tag = query_tag or selected()
if query_tag == nil then return end
for i, t in ipairs(capi.screen[query_tag.screen]:tags()) do
if t == query_tag then
return i
end
end
end
--- View next tag. This is the same as tag.viewidx(1).
-- @param screen The screen number.
function viewnext(screen)
return viewidx(1, screen)
end
--- View previous tag. This is the same a tag.viewidx(-1).
-- @param screen The screen number.
function viewprev(screen)
return viewidx(-1, screen)
end
--- View only a tag.
-- @param t The tag object.
function viewonly(t)
local tags = capi.screen[t.screen]:tags()
-- First, untag everyone except the viewed tag.
for _, tag in pairs(tags) do
if tag ~= t then
tag.selected = false
end
end
-- Then, set this one to selected.
-- We need to do that in 2 operations so we avoid flickering and several tag
-- selected at the same time.
t.selected = true
capi.screen[t.screen]:emit_signal("tag::history::update")
end
--- View only a set of tags.
-- @param tags A table with tags to view only.
-- @param screen Optional screen number of the tags.
function viewmore(tags, screen)
local screen_tags = capi.screen[screen or capi.mouse.screen]:tags()
for _, tag in ipairs(screen_tags) do
if not util.table.hasitem(tags, tag) then
tag.selected = false
end
end
for _, tag in ipairs(tags) do
tag.selected = true
end
capi.screen[screen]:emit_signal("tag::history::update")
end
--- Toggle selection of a tag
-- @param tag Tag to be toggled
function viewtoggle(t)
t.selected = not t.selected
capi.screen[t.screen]:emit_signal("tag::history::update")
end
--- Get tag data table.
-- @param tag The Tag.
-- @return The data table.
function getdata(tag)
return data.tags[tag]
end
--- Get a tag property.
-- @param tag The tag.
-- @param prop The property name.
-- @return The property.
function getproperty(tag, prop)
if data.tags[tag] then
return data.tags[tag][prop]
end
end
--- Set a tag property.
-- This properties are internal to awful. Some are used to draw taglist, or to
-- handle layout, etc.
-- @param tag The tag.
-- @param prop The property name.
-- @param value The value.
function setproperty(tag, prop, value)
if not data.tags[tag] then
data.tags[tag] = {}
end
data.tags[tag][prop] = value
tag:emit_signal("property::" .. prop)
end
--- Tag a client with the set of current tags.
-- @param c The client to tag.
-- @param startup Optional: don't do anything if true.
function withcurrent(c, startup)
if startup ~= true and c.sticky == false then
if #c:tags() == 0 then
c:tags(selectedlist(c.screen))
end
end
end
local function attached_add_signal_screen(screen, sig, func)
capi.screen[screen]:add_signal("tag::attach", function (s, tag)
tag:add_signal(sig, func)
end)
capi.screen[screen]:add_signal("tag::detach", function (s, tag)
tag:remove_signal(sig, func)
end)
for _, tag in ipairs(capi.screen[screen]:tags()) do
tag:add_signal(sig, func)
end
end
--- Add a signal to all attached tag and all tag that will be attached in the
-- future. When a tag is detach from the screen, its signal is removed.
-- @param screen The screen concerned, or all if nil.
function attached_add_signal(screen, ...)
if screen then
attached_add_signal_screen(screen, ...)
else
for screen = 1, capi.screen.count() do
attached_add_signal_screen(screen, ...)
end
end
end
-- Register standards signals
capi.client.add_signal("manage", function(c, startup)
-- If we are not managing this application at startup,
-- move it to the screen where the mouse is.
-- We only do it for "normal" windows (i.e. no dock, etc).
if not startup
and c.type ~= "desktop"
and c.type ~= "dock"
and c.type ~= "splash" then
if c.transient_for then
c.screen = c.transient_for.screen
if not c.sticky then
c:tags(c.transient_for:tags())
end
else
c.screen = capi.mouse.screen
end
end
c:add_signal("property::screen", withcurrent)
end)
capi.client.add_signal("manage", withcurrent)
for s = 1, capi.screen.count() do
capi.screen[s]:add_signal("tag::history::update", history.update)
end
setmetatable(_M, { __call = function (_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,421 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local math = math
local image = image
local pairs = pairs
local type = type
local setmetatable = setmetatable
local type = type
local capi =
{
awesome = awesome,
wibox = wibox,
widget = widget,
client = client,
}
local abutton = require("awful.button")
local beautiful = require("beautiful")
local util = require("awful.util")
local widget = require("awful.widget")
local mouse = require("awful.mouse")
local client = require("awful.client")
local layout = require("awful.widget.layout")
--- Titlebar module for awful
module("awful.titlebar")
-- Privata data
local data = setmetatable({}, { __mode = 'k' })
-- Predeclaration for buttons
local button_groups
local function button_callback_focus_raise_move(w, t)
capi.client.focus = t.client
t.client:raise()
mouse.client.move(t.client)
end
local function button_callback_move(w, t)
return mouse.client.move(t.client)
end
local function button_callback_resize(w, t)
return mouse.client.resize(t.client)
end
--- Create a standard titlebar.
-- @param c The client.
-- @param args Arguments.
-- modkey: the modkey used for the bindings.
-- fg: the foreground color.
-- bg: the background color.
-- fg_focus: the foreground color for focused window.
-- fg_focus: the background color for focused window.
-- width: the titlebar width
function add(c, args)
if not c or (c.type ~= "normal" and c.type ~= "dialog") then return end
if not args then args = {} end
if not args.height then args.height = capi.awesome.font_height * 1.5 end
local theme = beautiful.get()
if not args.widget then customwidget = {} else customwidget = args.widget end
-- Store colors
data[c] = {}
data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
data[c].width = args.width
data[c].font = args.font or theme.titlebar_font or theme.font
local tb = capi.wibox(args)
local title = capi.widget({ type = "textbox" })
if c.name then
title.text = "<span font_desc='" .. data[c].font .. "'> " ..
util.escape(c.name) .. " </span>"
end
-- Redirect relevant events to the client the titlebar belongs to
local bts = util.table.join(
abutton({ }, 1, button_callback_focus_raise_move),
abutton({ args.modkey }, 1, button_callback_move),
abutton({ args.modkey }, 3, button_callback_resize))
title:buttons(bts)
local appicon = capi.widget({ type = "imagebox" })
appicon.image = c.icon
-- for each button group, call create for the client.
-- if a button set is created add the set to the
-- data[c].button_sets for late updates and add the
-- individual buttons to the array part of the widget
-- list
local widget_list = {
layout = layout.horizontal.rightleft
}
local iw = 1
local is = 1
data[c].button_sets = {}
for i = 1, #button_groups do
local set = button_groups[i].create(c, args.modkey, theme)
if (set) then
data[c].button_sets[is] = set
is = is + 1
for n,b in pairs(set) do
widget_list[iw] = b
iw = iw + 1
end
end
end
tb.widgets = {
widget_list,
customwidget,
{
appicon = appicon,
title = title,
layout = layout.horizontal.flex
},
layout = layout.horizontal.rightleft
}
c.titlebar = tb
c:add_signal("property::icon", update)
c:add_signal("property::name", update)
c:add_signal("property::sticky", update)
c:add_signal("property::floating", update)
c:add_signal("property::ontop", update)
c:add_signal("property::maximized_vertical", update)
c:add_signal("property::maximized_horizontal", update)
update(c)
end
--- Update a titlebar. This should be called in some hooks.
-- @param c The client to update.
-- @param prop The property name which has changed.
function update(c)
if c.titlebar and data[c] then
local widgets = c.titlebar.widgets
if widgets[3].title then
widgets[3].title.text = "<span font_desc='" .. data[c].font ..
"'> ".. util.escape(c.name or "<unknown>") .. " </span>"
end
if widgets[3].appicon then
widgets[3].appicon.image = c.icon
end
if capi.client.focus == c then
c.titlebar.fg = data[c].fg_focus
c.titlebar.bg = data[c].bg_focus
else
c.titlebar.fg = data[c].fg
c.titlebar.bg = data[c].bg
end
-- iterated of all registered button_sets and update
local sets = data[c].button_sets
for i = 1, #sets do
sets[i].update(c,prop)
end
end
end
--- Remove a titlebar from a client.
-- @param c The client.
function remove(c)
c.titlebar = nil
data[c] = nil
end
-- Create a new button for the toolbar
-- @param c The client of the titlebar
-- @param name The base name of the button (i.e. close)
-- @param modkey ... you know that one, don't you?
-- @param theme The theme from beautifull. Used to get the image paths
-- @param state The state the button is associated to. Containse path the action and info about the image
local function button_new(c, name, modkey, theme, state)
local bts = abutton({ }, 1, nil, state.action)
-- get the image path from the theme. Only return a button if we find an image
local img
img = "titlebar_" .. name .. "_button_" .. state.img
img = theme[img]
if not img then return end
img = image(img)
if not img then return end
-- now create the button
local bname = name .. "_" .. state.idx
local button = widget.button({ image = img })
if not button then return end
local rbts = button:buttons()
for k, v in pairs(rbts) do
bts[#bts + 1] = v
end
button:buttons(bts)
button.visible = false
return button
end
-- Update the buttons in a button group
-- @param s The button group to update
-- @param c The client of the titlebar
-- @param p The property that has changed
local function button_group_update(s,c,p)
-- hide the currently active button, get the new state and show the new button
local n = s.select_state(c,p)
if n == nil then return end
if (s.active ~= nil) then s.active.visible = false end
s.active = s.buttons[n]
s.active.visible = true
end
-- Create all buttons in a group
-- @param c The client of the titlebar
-- @param group The button group to create the buttons for
-- @param modkey ...
-- @param theme Theme for the image paths
local function button_group_create(c, group, modkey, theme )
local s = {}
s.name = group.name
s.select_state = group.select_state
s.buttons = {
layout = layout.horizontal.rightleft
}
for n,state in pairs(group.states) do
s.buttons[n] = button_new(c, s.name, modkey, theme, state)
if (s.buttons[n] == nil) then return end
for a,v in pairs(group.attributes) do
s.buttons[n][a] = v
end
end
function s.update(c,p) button_group_update(s,c,p) end
return s
end
-- Builds a new button group
-- @param name The base name for the buttons in the group (i.e. "close")
-- @param attrs Common attributes for the buttons (i.e. {align = "right")
-- @param sfn State select function.
-- @param args The states of the button
local function button_group(name, attrs, sfn, ...)
local s = {}
s.name = name
s.select_state = sfn
s.attributes = attrs
s.states = {}
for i, state in pairs({...}) do
s.states[state.idx] = state
end
function s.create(c,modkey, theme) return button_group_create(c,s,modkey, theme) end
return s
end
-- Select a state for a client based on an attribute of the client and whether it has focus
-- @param c The client of the titlebar
-- @param p The property that has changed
-- @param a The property to check
local function select_state(c,p,a)
if (c == nil) then return "n/i" end
if capi.client.focus == c then
if c[a] then
return "f/a"
else
return "f/i"
end
else
if c[a] then
return "n/a"
else
return "n/i"
end
end
end
-- Select a state for a client based on whether it's floating or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_floating(c,p)
if not c then return end
if capi.client.focus == c then
if client.floating.get(c) then
return "f/a"
end
return "f/i"
end
if client.floating.get(c) then
return "n/a"
end
return "n/i"
end
-- Select a state for a client based on whether it's maximized or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_maximized(c,p)
if (c == nil) then return "n/i" end
if capi.client.focus == c then
if c.maximized_horizontal or c.maximized_vertical then
return "f/a"
else
return "f/i"
end
else
if c.maximized_horizontal or c.maximized_vertical then
return "n/a"
else
return "n/i"
end
end
end
-- Select a state for a client based on whether it has focus or not
-- @param c The client of the titlebar
-- @param p The property that has changed
local function select_state_focus(c,p)
if c and capi.client.focus == c then
return "f"
end
return "n"
end
-- These are the predefined button groups
-- A short explanation using 'close_buttons' as an example:
-- "close" : name of the button, the images for this button are taken from the
-- theme variables titlebar_close_button_...
-- { align ... : attributes of all the buttons
-- select_state_focus : This function returns a short string used to describe
-- the state. In this case either "n" or "f" depending on
-- the focus state of the client. These strings can be
-- choosen freely but the< must match one of the idx fuekds
-- of the states below
-- { idx = "n" ... : This is the state of the button for the 'unfocussed'
-- (normal) state. The idx = "n" parameter connects this
-- button to the return value of the 'select_state_focus'
-- function. The img = "normal" parameter is used to
-- determine its image. In this case the iamge is taken from
-- the theme variable "titlebar_close_button_normal".
-- Finally the last parameter is the action for mouse
-- button 1
local ontop_buttons = button_group("ontop",
{ align = "right" },
function(c,p) return select_state(c, p, "ontop") end,
{ idx = "n/i", img = "normal_inactive",
action = function(w, t) t.client.ontop = true end },
{ idx = "f/i", img = "focus_inactive",
action = function(w, t) t.client.ontop = true end },
{ idx = "n/a", img = "normal_active",
action = function(w, t) t.client.ontop = false end },
{ idx = "f/a", img = "focus_active",
action = function(w, t) t.client.ontop = false end })
local sticky_buttons = button_group("sticky",
{ align = "right" },
function(c,p) return select_state(c,p,"sticky") end,
{ idx = "n/i", img = "normal_inactive",
action = function(w, t) t.client.sticky = true end },
{ idx = "f/i", img = "focus_inactive",
action = function(w, t) t.client.sticky = true end },
{ idx = "n/a", img = "normal_active",
action = function(w, t) t.client.sticky = false end },
{ idx = "f/a", img = "focus_active",
action = function(w, t) t.client.sticky = false end })
local maximized_buttons = button_group("maximized",
{ align = "right" },
select_state_maximized,
{ idx = "n/i", img = "normal_inactive",
action = function(w, t) t.client.maximized_horizontal = true
t.client.maximized_vertical = true end },
{ idx = "f/i", img = "focus_inactive",
action = function(w, t) t.client.maximized_horizontal = true
t.client.maximized_vertical = true end },
{ idx = "n/a", img = "normal_active",
action = function(w, t) t.client.maximized_horizontal = false
t.client.maximized_vertical = false end },
{ idx = "f/a", img = "focus_active",
action = function(w, t) t.client.maximized_horizontal = false
t.client.maximized_vertical = false end })
local close_buttons = button_group("close",
{ align = "left" },
select_state_focus,
{ idx = "n", img = "normal",
action = function (w, t) t.client:kill() end },
{ idx = "f", img = "focus",
action = function (w, t) t.client:kill() end })
local function floating_update(w, t)
client.floating.toggle(t.client)
end
local floating_buttons = button_group("floating",
{ align = "right"},
select_state_floating,
{ idx = "n/i", img = "normal_inactive", action = floating_update },
{ idx = "f/i", img = "focus_inactive", action = floating_update },
{ idx = "n/a", img = "normal_active", action = floating_update },
{ idx = "f/a", img = "focus_active", action = floating_update })
button_groups = { close_buttons,
ontop_buttons,
sticky_buttons,
maximized_buttons,
floating_buttons }
-- Register standards hooks
capi.client.add_signal("focus", update)
capi.client.add_signal("unfocus", update)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,233 @@
-------------------------------------------------------------------------
-- @author Sébastien Gross &lt;seb•ɱɩɲʋʃ•awesome•ɑƬ•chezwam•ɖɵʈ•org&gt
-- @copyright 2009 Sébastien Gross
-- @release v3.4.10
-------------------------------------------------------------------------
local mouse = mouse
local widget = widget
local wibox = wibox
local screen = screen
local timer = timer
local a_placement = require("awful.placement")
local beautiful = require("beautiful")
local setmetatable = setmetatable
local ipairs = ipairs
--- Tooltip module for awesome objects.
-- A tooltip is a small hint displayed when the mouse cursor
-- hovers a specific item.
-- In awesome, a tooltip can be linked with almost any
-- object having a <code>add_signal()</code> method and receiving
-- <code>mouse::enter</code> and <code>mouse::leave</code> signals.
-- <p>How to create a tooltip?<br/>
-- <code>
-- myclock = awful.widget.textclock({}, "%T", 1)<br/>
-- myclock_t = awful.tooltip({<br/>
-- objects = { K },<br/>
-- timer_function = function()<br/>
-- return os.date("Today is %A %B %d %Y\nThe time is %T")<br/>
-- end,<br/>
-- })<br/>
-- </code>
-- </p>
-- <p>How to add the same tooltip to several objects?<br/>
-- <code>
-- myclock_t:add_to_object(obj1)<br/>
-- myclock_t:add_to_object(obj2)<br/>
-- </code>
-- Now the same tooltip is attached to <code>K</code>, <code>obj1</code>,
-- <code>obj2</code>.<br/>
-- </p>
-- <p>How to remove tooltip from many objects?<br/>
-- <code>
-- myclock_t:remove_from_object(obj1)<br/>
-- myclock_t:remove_from_object(obj2)<br/>
-- </code>
-- Now the same tooltip is only attached to <code>K</code>.<br/>
-- </p>
module("awful.tooltip")
local data = setmetatable({}, { __mode = 'k' })
--- Tooltip object definition.
-- @name tooltip
-- @field wibox The wibox displaying the tooltip.
-- @field visible True if tooltip is visible.
-- @class table
-- Tooltip private data.
-- @name awful.tooltip.data
-- @field fg tooltip foreground color.
-- @field font Tooltip font.
-- @field hide The hide() function.
-- @field show The show() function.
-- @field timer The text update timer.
-- @field timer_function The text update timer function.
-- Place to tooltip on th screen.
-- @param self A tooltip object.
local function place(self)
a_placement.under_mouse(self.wibox)
a_placement.no_offscreen(self.wibox)
end
-- Place the tooltip under the mouse.
-- @param self A tooltip object.
local function set_geometry(self)
local my_geo = self.wibox:geometry()
-- calculate width / height
n_s = self.wibox.widgets[1]:extents()
if my_geo.width ~= n_s.width or my_geo.height ~= n_s.height then
self.wibox:geometry(n_s)
place(self)
end
if not self.wibox.visible then
place(self)
end
end
-- Show a tooltip.
-- @param self The tooltip to show.
local function show(self)
-- do nothing if the tooltip is already shown
if self.visible then return end
-- make sure the tooltip is on the same screen as the mouse
self.wibox.screen = mouse.screen
if data[self].timer then
if not data[self].timer.started then
data[self].timer_function()
data[self].timer:start()
end
end
set_geometry(self)
self.wibox.visible = true
self.visible = true
end
-- Hide a tooltip.
-- @param self The tooltip to hide.
local function hide(self)
-- do nothing if the tooltip is already hidden
if not self.visible then return end
if data[self].timer then
if data[self].timer.started then
data[self].timer:stop()
end
end
self.visible = false
self.wibox.visible = false
end
--- Change displayed text.
-- @param self The tooltip object.
-- @param text New tooltip text.
local function set_text(self, text)
self.wibox.widgets[1].text = '<span color="' .. data[self].fg
.. '" font_desc="' .. data[self].font .. '">' .. text .. "</span>"
end
--- Change the tooltip's update interval.
-- @param self A tooltip object.
-- @param timeout The timeout value.
local function set_timeout(self, timeout)
if data[self].timer then
data[self].timer.timeout = timeout
end
end
-- Load Default values.
-- @param self A tooltip object.
local function set_defaults(self)
self.wibox.border_width = beautiful.tooltip_border_width or beautiful.border_width or 1
self.wibox.border_color = beautiful.tooltip_border_color or beautiful.border_normal or "#ffcb60"
self.wibox.opacity = beautiful.tooltip_opacity or 1
self.wibox.bg = beautiful.tooltip_bg_color or beautiful.bg_focus or "#ffcb60"
data[self].fg = beautiful.tooltip_fg_color or beautiful.fg_focus or "#000000"
data[self].font = beautiful.tooltip_font or beautiful.font or "terminus 6"
end
--- Add tooltip to an object.
-- @param self The tooltip.
-- @param object An object.
local function add_to_object(self, object)
object:add_signal("mouse::enter", data[self].show)
object:add_signal("mouse::leave", data[self].hide)
end
--- Remove tooltip from an object.
-- @param self The tooltip.
-- @param object An object.
local function remove_from_object(self, object)
object:remove_signal("mouse::enter", data[self].show)
object:remove_signal("mouse::leave", data[self].hide)
end
--- Create a new tooltip and link it to a widget.
-- @param args Arguments for tooltip creation may containt:<br/>
-- <code>timeout</code>: The timeout value for update_func.<br/>
-- <code>timer_function</code>: A function to dynamicaly change the tooltip
-- text.<br/>
-- <code>objects</code>: A list of objects linked to the tooltip.<br/>
-- @return The created tooltip.
-- @see add_to_object
-- @see set_timeout
-- @see set_text
local function new(args)
local self = {
wibox = wibox({ }),
visible = false,
}
local my_textbox = widget({
type = "textbox",
name = "tooltip_textbox",
align="right"})
-- private data
data[self] = {
show = function() show(self) end,
hide = function() hide(self) end
}
-- export functions
self.set_text = set_text
self.set_timeout = set_timeout
self.add_to_object = add_to_object
self.remove_from_object = remove_from_object
set_defaults(self)
-- setup the timer action only if needed
if args.timer_function then
data[self].timer = timer { timeout = args.timeout and args.timeout or 1 }
data[self].timer_function = function()
self:set_text(args.timer_function())
set_geometry(self)
end
data[self].timer:add_signal("timeout", data[self].timer_function)
end
-- set tooltip properties
self.wibox.visible = false
-- Who want a non ontop tooltip ?
self.wibox.ontop = true
self.wibox.widgets = { my_textbox }
-- add some signals on both the tooltip and widget
self.wibox:add_signal("mouse::enter", data[self].hide)
-- Add tooltip to objects
if args.objects then
for _, object in ipairs(args.objects) do
self:add_to_object(object)
end
end
return self
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: ft=lua:et:sw=4:ts=4:sts=4:enc=utf-8:tw=78

View File

@ -0,0 +1,347 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local os = os
local io = io
local assert = assert
local loadstring = loadstring
local loadfile = loadfile
local debug = debug
local pairs = pairs
local ipairs = ipairs
local type = type
local rtable = table
local pairs = pairs
local string = string
local capi =
{
awesome = awesome,
mouse = mouse
}
--- Utility module for awful
module("awful.util")
table = {}
shell = os.getenv("SHELL") or "/bin/sh"
function deprecate(see)
io.stderr:write("W: awful: function is deprecated")
if see then
io.stderr:write(", see " .. see)
end
io.stderr:write("\n")
io.stderr:write(debug.traceback())
end
--- Strip alpha part of color.
-- @param color The color.
-- @return The color without alpha channel.
function color_strip_alpha(color)
if color:len() == 9 then
color = color:sub(1, 7)
end
return color
end
--- Make i cycle.
-- @param t A length.
-- @param i An absolute index to fit into #t.
-- @return The object at new index.
function cycle(t, i)
while i > t do i = i - t end
while i < 1 do i = i + t end
return i
end
--- Create a directory
-- @param dir The directory.
-- @return mkdir return code
function mkdir(dir)
return os.execute("mkdir -p " .. dir)
end
--- Spawn a program.
-- @param cmd The command.
-- @param sn Enable startup-notification.
-- @param screen The screen where to spawn window.
-- @return The awesome.spawn return value.
function spawn(cmd, sn, screen)
if cmd and cmd ~= "" then
if sn == nil then sn = true end
return capi.awesome.spawn(cmd, sn, screen or capi.mouse.screen)
end
end
--- Spawn a program using the shell.
-- @param cmd The command.
-- @param screen The screen where to run the command.
function spawn_with_shell(cmd, screen)
if cmd and cmd ~= "" then
cmd = shell .. " -c \"" .. cmd .. "\""
return capi.awesome.spawn(cmd, false, screen or capi.mouse.screen)
end
end
--- Read a program output and returns its output as a string.
-- @param cmd The command to run.
-- @return A string with the program output, or the error if one occured.
function pread(cmd)
if cmd and cmd ~= "" then
local f, err = io.popen(cmd, 'r')
if f then
local s = f:read("*all")
f:close()
return s
else
return err
end
end
end
--- Eval Lua code.
-- @return The return value of Lua code.
function eval(s)
return assert(loadstring(s))()
end
local xml_entity_names = { ["'"] = "&apos;", ["\""] = "&quot;", ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;" };
--- Escape a string from XML char.
-- Useful to set raw text in textbox.
-- @param text Text to escape.
-- @return Escape text.
function escape(text)
return text and text:gsub("['&<>\"]", xml_entity_names) or nil
end
local xml_entity_chars = { lt = "<", gt = ">", nbsp = " ", quot = "\"", apos = "'", ndash = "-", mdash = "-", amp = "&" };
--- Unescape a string from entities.
-- @param text Text to unescape.
-- @return Unescaped text.
function unescape(text)
return text and text:gsub("&(%a+);", xml_entity_chars) or nil
end
--- Check if a file is a Lua valid file.
-- This is done by loading the content and compiling it with loadfile().
-- @param path The file path.
-- @return A function if everything is alright, a string with the error
-- otherwise.
function checkfile(path)
local f, e = loadfile(path)
-- Return function if function, otherwise return error.
if f then return f end
return e
end
--- Try to restart awesome.
-- It checks if the configuration file is valid, and then restart if it's ok.
-- If it's not ok, the error will be returned.
-- @return Never return if awesome restart, or return a string error.
function restart()
local c = checkfile(capi.awesome.conffile)
if type(c) ~= "function" then
return c
end
capi.awesome.restart()
end
--- Get the user's config or cache dir.
-- It first checks XDG_CONFIG_HOME / XDG_CACHE_HOME, but then goes with the
-- default paths.
-- @param d The directory to get (either "config" or "cache").
-- @return A string containing the requested path.
function getdir(d)
if d == "config" then
local dir = os.getenv("XDG_CONFIG_HOME")
if dir then
return dir .. "/awesome"
end
return os.getenv("HOME") .. "/.config/awesome"
elseif d == "cache" then
local dir = os.getenv("XDG_CACHE_HOME")
if dir then
return dir .. "/awesome"
end
return os.getenv("HOME").."/.cache/awesome"
end
end
--- Check if file exists and is readable.
-- @param filename The file path
-- @return True if file exists and readable.
function file_readable(filename)
local file = io.open(filename)
if file then
io.close(file)
return true
end
return false
end
local function subset_mask_apply(mask, set)
local ret = {}
for i = 1, #set do
if mask[i] then
rtable.insert(ret, set[i])
end
end
return ret
end
local function subset_next(mask)
local i = 1
while i <= #mask and mask[i] do
mask[i] = false
i = i + 1
end
if i <= #mask then
mask[i] = 1
return true
end
return false
end
--- Return all subsets of a specific set.
-- This function, giving a set, will return all subset it.
-- For example, if we consider a set with value { 10, 15, 34 },
-- it will return a table containing 2^n set:
-- { }, { 10 }, { 15 }, { 34 }, { 10, 15 }, { 10, 34 }, etc.
-- @param set A set.
-- @return A table with all subset.
function subsets(set)
local mask = {}
local ret = {}
for i = 1, #set do mask[i] = false end
-- Insert the empty one
rtable.insert(ret, {})
while subset_next(mask) do
rtable.insert(ret, subset_mask_apply(mask, set))
end
return ret
end
--- Join all tables given as parameters.
-- This will iterate all tables and insert all their keys into a new table.
-- @param args A list of tables to join
-- @return A new table containing all keys from the arguments.
function table.join(...)
local ret = {}
for i, t in ipairs({...}) do
if t then
for k, v in pairs(t) do
if type(k) == "number" then
rtable.insert(ret, v)
else
ret[k] = v
end
end
end
end
return ret
end
--- Check if a table has an item and return its key.
-- @param t The table.
-- @param item The item to look for in values of the table.
-- @return The key were the item is found, or nil if not found.
function table.hasitem(t, item)
for k, v in pairs(t) do
if v == item then
return k
end
end
end
--- Split a string into multiple lines
-- @param text String to wrap.
-- @param width Maximum length of each line. Default: 72.
-- @param indent Number of spaces added before each wrapped line. Default: 0.
-- @return The string with lines wrapped to width.
function linewrap(text, width, indent)
local text = text or ""
local width = width or 72
local indent = indent or 0
local pos = 1
return text:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
if fi - pos > width then
pos = st
return "\n" .. string.rep(" ", indent) .. word
end
end)
end
--- Get a sorted table with all integer keys from a table
-- @param t the table for which the keys to get
-- @return A table with keys
function table.keys(t)
local keys = { }
for k, _ in pairs(t) do
rtable.insert(keys, k)
end
rtable.sort(keys, function (a, b)
return type(a) == type(b) and a < b or false
end)
return keys
end
--- Filter a tables keys for certain content types
-- @param t The table to retrieve the keys for
-- @param ... the types to look for
-- @return A filtered table with keys
function table.keys_filter(t, ...)
local keys = table.keys(t)
local keys_filtered = { }
for _, k in pairs(keys) do
for _, et in pairs({...}) do
if type(t[k]) == et then
rtable.insert(keys_filtered, k)
break
end
end
end
return keys_filtered
end
--- Reverse a table
-- @param t the table to reverse
-- @return the reversed table
function table.reverse(t)
local tr = { }
-- reverse all elements with integer keys
for _, v in ipairs(t) do
rtable.insert(tr, 1, v)
end
-- add the remaining elements
for k, v in pairs(t) do
if type(k) ~= "number" then
tr[k] = v
end
end
return tr
end
--- Clone a table
-- @param t the table to clone
-- @return a clone of t
function table.clone(t)
local c = { }
for k, v in pairs(t) do
c[k] = v
end
return c
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,345 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local capi =
{
awesome = awesome,
screen = screen,
wibox = wibox,
client = client
}
local setmetatable = setmetatable
local tostring = tostring
local ipairs = ipairs
local table = table
local type = type
local image = image
local error = error
--- Wibox module for awful.
-- This module allows you to easily create wibox and attach them to the edge of
-- a screen.
module("awful.wibox")
-- Array of table with wiboxes inside.
-- It's an array so it is ordered.
local wiboxes = {}
--- Get a wibox position if it has been set, or return top.
-- @param wibox The wibox
-- @return The wibox position.
function get_position(wibox)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
return wprop.position
end
end
return "top"
end
--- Put a wibox on a screen at this position.
-- @param wibox The wibox to attach.
-- @param position The position: top, bottom left or right.
-- @param screen If the wibox it not attached to a screen, specified on which
-- screen the position should be set.
function set_position(wibox, position, screen)
local screen = screen or wibox.screen or 1
local area = capi.screen[screen].geometry
-- The "length" of a wibox is always chosen to be the optimal size
-- (non-floating).
-- The "width" of a wibox is kept if it exists.
if position == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif position == "left" then
wibox.x = area.x
elseif position == "bottom" then
wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
elseif position == "top" then
wibox.y = area.y
end
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
wprop.position = position
break
end
end
end
-- Reset all wiboxes positions.
local function update_all_wiboxes_position()
for _, wprop in ipairs(wiboxes) do
set_position(wprop.wibox, wprop.position)
end
end
local function call_wibox_position_hook_on_prop_update(w)
update_all_wiboxes_position()
end
local function wibox_update_strut(wibox)
for _, wprop in ipairs(wiboxes) do
if wprop.wibox == wibox then
if not wibox.visible then
wibox:struts { left = 0, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "top" then
wibox:struts { left = 0, right = 0, bottom = 0, top = wibox.height + 2 * wibox.border_width }
elseif wprop.position == "bottom" then
wibox:struts { left = 0, right = 0, bottom = wibox.height + 2 * wibox.border_width, top = 0 }
elseif wprop.position == "left" then
wibox:struts { left = wibox.width + 2 * wibox.border_width, right = 0, bottom = 0, top = 0 }
elseif wprop.position == "right" then
wibox:struts { left = 0, right = wibox.width + 2 * wibox.border_width, bottom = 0, top = 0 }
end
break
end
end
end
--- Attach a wibox to a screen.
-- If a wibox is attached, it will be automatically be moved when other wiboxes
-- will be attached.
-- @param wibox The wibox to attach.
-- @param position The position of the wibox: top, bottom, left or right.
function attach(wibox, position)
-- Store wibox as attached in a weak-valued table
local wibox_prop_table
-- Start from end since we sometimes remove items
for i = #wiboxes, 1, -1 do
-- Since wiboxes are stored as weak value, they can disappear.
-- If they did, remove their entries
if wiboxes[i].wibox == nil then
table.remove(wiboxes, i)
elseif wiboxes[i].wibox == wibox then
wibox_prop_table = wiboxes[i]
-- We could break here, but well, let's check if there is no other
-- table with their wiboxes been garbage collected.
end
end
if not wibox_prop_table then
table.insert(wiboxes, setmetatable({ wibox = wibox, position = position }, { __mode = 'v' }))
else
wibox_prop_table.position = position
end
wibox:add_signal("property::width", wibox_update_strut)
wibox:add_signal("property::height", wibox_update_strut)
wibox:add_signal("property::visible", wibox_update_strut)
wibox:add_signal("property::screen", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::width", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::height", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::visible", call_wibox_position_hook_on_prop_update)
wibox:add_signal("property::border_width", call_wibox_position_hook_on_prop_update)
end
--- Align a wibox.
-- @param wibox The wibox.
-- @param align The alignment: left, right or center.
-- @param screen If the wibox is not attached to any screen, you can specify the
-- screen where to align. Otherwise 1 is assumed.
function align(wibox, align, screen)
local position = get_position(wibox)
local screen = screen or wibox.screen or 1
local area = capi.screen[screen].workarea
if position == "right" then
if align == "right" then
wibox.y = area.y
elseif align == "left" then
wibox.y = area.y + area.height - (wibox.height + 2 * wibox.border_width)
elseif align == "center" then
wibox.y = area.y + (area.height - wibox.height) / 2
end
elseif position == "left" then
if align == "right" then
wibox.y = (area.y + area.height) - (wibox.height + 2 * wibox.border_width)
elseif align == "left" then
wibox.y = area.y
elseif align == "center" then
wibox.y = area.y + (area.height - wibox.height) / 2
end
elseif position == "bottom" then
if align == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif align == "left" then
wibox.x = area.x
elseif align == "center" then
wibox.x = area.x + (area.width - wibox.width) / 2
end
elseif position == "top" then
if align == "right" then
wibox.x = area.x + area.width - (wibox.width + 2 * wibox.border_width)
elseif align == "left" then
wibox.x = area.x
elseif align == "center" then
wibox.x = area.x + (area.width - wibox.width) / 2
end
end
-- Update struts regardless of changes
wibox_update_strut(wibox)
end
--- Stretch a wibox so it takes all screen width or height.
-- @param wibox The wibox.
-- @param screen The screen to stretch on, or the wibox screen.
function stretch(wibox, screen)
local screen = screen or wibox.screen
if screen then
local position = get_position(wibox)
local area = capi.screen[screen].workarea
if position == "right" or position == "left" then
wibox.height = area.height - (2 * wibox.border_width)
wibox.y = area.y
else
wibox.width = area.width - (2 * wibox.border_width)
wibox.x = area.x
end
end
end
--- Create a new wibox and attach it to a screen edge.
-- @see capi.wibox
-- @param args A table with standard arguments to wibox() creator.
-- You can add also position key with value top, bottom, left or right.
-- You can also use width or height in % and set align to center, right or left.
-- You can also set the screen key with a screen number to attach the wibox.
-- If not specified, 1 is assumed.
-- @return The wibox created.
function new(arg)
local arg = arg or {}
local position = arg.position or "top"
local has_to_stretch = true
-- Empty position and align in arg so we are passing deprecation warning
arg.position = nil
if position ~= "top" and position ~="bottom"
and position ~= "left" and position ~= "right" then
error("Invalid position in awful.wibox(), you may only use"
.. " 'top', 'bottom', 'left' and 'right'")
end
-- Set default size
if position == "left" or position == "right" then
arg.width = arg.width or capi.awesome.font_height * 1.5
if arg.height then
has_to_stretch = false
if arg.screen then
local hp = tostring(arg.height):match("(%d+)%%")
if hp then
arg.height = capi.screen[arg.screen].geometry.height * hp / 100
end
end
end
else
arg.height = arg.height or capi.awesome.font_height * 1.5
if arg.width then
has_to_stretch = false
if arg.screen then
local wp = tostring(arg.width):match("(%d+)%%")
if wp then
arg.width = capi.screen[arg.screen].geometry.width * wp / 100
end
end
end
end
local w = capi.wibox(arg)
if position == "left" then
w.orientation = "north"
elseif position == "right" then
w.orientation = "south"
end
w.screen = arg.screen or 1
attach(w, position)
if has_to_stretch then
stretch(w)
else
align(w, arg.align)
end
set_position(w, position)
return w
end
local function do_rounded_corners(width, height, corner)
local img = image.argb32(width, height, nil)
-- The image starts completely black which is fully opaque for our use
local function transp_rect(x, y)
img:draw_rectangle(x, y, corner, corner, true, "#ffffff")
end
local function opaque_circle(x, y)
-- x, y are the center of the circle
img:draw_circle(x, y, corner, corner, true, "#000000")
end
-- Upper left corner
-- First make a 'corner times corner' rectangle transparent
transp_rect(0, 0)
-- Then add the rounded corner
opaque_circle(corner, corner)
-- Upper right corner
transp_rect(width - corner, 0)
opaque_circle(width - corner - 1, corner)
-- Bottom left corner
transp_rect(0, height - corner)
opaque_circle(corner, height - corner - 1)
-- Bottom right corner
transp_rect(width - corner, height - corner)
opaque_circle(width - corner - 1, height - corner - 1)
return img
end
--- Add rounded corners to a wibox
-- @param wibox The wibox.
-- @param corner_size The size in pixel of the rounded corners.
function rounded_corners(wibox, corner_size)
local border = wibox.border_width
-- Corners can't be larger than half the wibox' space
if wibox.width / 2 < corner_size then
corner_size = wibox.width / 2
end
if wibox.height / 2 < corner_size then
corner_size = wibox.height / 2
end
wibox.shape_clip = do_rounded_corners(wibox.width, wibox.height, corner_size)
wibox.shape_bounding = do_rounded_corners(wibox.width + border * 2, wibox.height + border * 2, corner_size + border)
end
local function update_wiboxes_on_struts(c)
local struts = c:struts()
if struts.left ~= 0 or struts.right ~= 0
or struts.top ~= 0 or struts.bottom ~= 0 then
update_all_wiboxes_position()
end
end
-- Hook registered to reset all wiboxes position.
capi.client.add_signal("manage", function(c)
update_wiboxes_on_struts(c)
c:add_signal("property::struts", update_wiboxes_on_struts)
end)
capi.client.add_signal("unmanage", update_wiboxes_on_struts)
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,45 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local type = type
local button = require("awful.button")
local capi = { image = image,
widget = widget,
mouse = mouse }
module("awful.widget.button")
--- Create a button widget. When clicked, the image is deplaced to make it like
-- a real button.
-- @param args Standard widget table arguments, plus image for the image path or
-- the image object.
-- @return A textbox widget configured as a button.
function new(args)
if not args or not args.image then return end
local img_release
if type(args.image) == "string" then
img_release = capi.image(args.image)
elseif type(args.image) == "image" then
img_release = args.image
else
return
end
local img_press = img_release:crop(-2, -2, img_release.width, img_release.height)
args.type = "imagebox"
local w = capi.widget(args)
w.image = img_release
w:buttons(button({}, 1, function () w.image = img_press end, function () w.image = img_release end))
w:add_signal("mouse::leave", function () w.image = img_release end)
w:add_signal("mouse::enter", function ()
if capi.mouse.coords().buttons[1] then w.image = img_press end
end)
return w
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,98 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local math = math
local type = type
local pcall = pcall
local ipairs = ipairs
local setmetatable = setmetatable
local capi = { widget = widget, button = button }
--- Common widget code
module("awful.widget.common")
-- Private structures
tagwidgets = setmetatable({}, { __mode = 'k' })
function list_update(w, buttons, label, data, widgets, objects)
-- Hack: if it has been registered as a widget in a wibox,
-- it's w.len since __len meta does not work on table until Lua 5.2.
-- Otherwise it's standard #w.
local len = (w.len or #w) / 2
-- Add more widgets
if len < #objects then
for i = len * 2 + 1, #objects * 2, 2 do
local ib = capi.widget({ type = "imagebox", align = widgets.imagebox.align })
local tb = capi.widget({ type = "textbox", align = widgets.textbox.align })
w[i] = ib
w[i + 1] = tb
w[i + 1]:margin({ left = widgets.textbox.margin.left, right = widgets.textbox.margin.right })
w[i + 1].bg_resize = widgets.textbox.bg_resize or false
w[i + 1].bg_align = widgets.textbox.bg_align or ""
if type(objects[math.floor(i / 2) + 1]) == "tag" then
tagwidgets[ib] = objects[math.floor(i / 2) + 1]
tagwidgets[tb] = objects[math.floor(i / 2) + 1]
end
end
-- Remove widgets
elseif len > #objects then
for i = #objects * 2 + 1, len * 2, 2 do
w[i] = nil
w[i + 1] = nil
end
end
-- update widgets text
for k = 1, #objects * 2, 2 do
local o = objects[(k + 1) / 2]
if buttons then
-- Use a local variable so that the garbage collector doesn't strike
-- between now and the :buttons() call.
local btns = data[o]
if not btns then
btns = {}
data[o] = btns
for kb, b in ipairs(buttons) do
-- Create a proxy button object: it will receive the real
-- press and release events, and will propagate them the the
-- button object the user provided, but with the object as
-- argument.
local btn = capi.button { modifiers = b.modifiers, button = b.button }
btn:add_signal("press", function () b:emit_signal("press", o) end)
btn:add_signal("release", function () b:emit_signal("release", o) end)
btns[#btns + 1] = btn
end
end
w[k]:buttons(btns)
w[k + 1]:buttons(btns)
end
local text, bg, bg_image, icon = label(o)
-- Check if we got a valid text here, it might contain e.g. broken utf8.
if not pcall(function() w[k + 1].text = text end) then
w[k + 1].text = "<i>Invalid</i>"
end
w[k + 1].bg, w[k + 1].bg_image = bg, bg_image
w[k].bg, w[k].image = bg, icon
if not w[k + 1].text then
w[k+1].visible = false
else
w[k+1].visible = true
end
if not w[k].image then
w[k].visible = false
else
w[k].visible = true
end
end
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,301 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local table = table
local type = type
local capi = { image = image,
widget = widget }
local layout = require("awful.widget.layout")
--- A graph widget.
module("awful.widget.graph")
local data = setmetatable({}, { __mode = "k" })
--- Set the graph border color.
-- If the value is nil, no border will be drawn.
-- @name set_border_color
-- @class function
-- @param graph The graph.
-- @param color The border color to set.
--- Set the graph foreground color as a gradient.
-- @name set_gradient_colors
-- @class function
-- @param graph The graph.
-- @param gradient_colors A table with gradients colors. The distance between each color
-- can also be specified. Example: { "red", "blue" } or { "red", "green",
-- "blue", blue = 10 } to specify blue distance from other colors.
--- Set the graph foreground colors gradient angle. Default is 270 degrees
-- (horizontal).
-- @name set_gradient_angle
-- @class function
-- @param graph The graph.
-- @param gradient_angle Angle of gradient in degrees.
--- Set the graph foreground color.
-- @name set_color
-- @class function
-- @param graph The graph.
-- @param color The graph color.
--- Set the graph background color.
-- @name set_background_color
-- @class function
-- @param graph The graph.
-- @param color The graph background color.
--- Set the maximum value the graph should handle.
-- If "scale" is also set, the graph never scales up below this value, but it
-- automatically scales down to make all data fit.
-- @name set_max_value
-- @class function
-- @param graph The graph.
-- @param value The value.
--- Set the graph to automatically scale its values. Default is false.
-- @name set_scale
-- @class function
-- @param graph The graph.
-- @param scale A boolean value
--- Set the graph to draw stacks. Default is false.
-- @name set_stack
-- @class function
-- @param graph The graph.
-- @param stack A boolean value.
--- Set the graph stacking colors. Order matters.
-- @name set_stack_colors
-- @class function
-- @param graph The graph.
-- @param stack_colors A table with stacking colors.
local properties = { "width", "height", "border_color", "stack",
"stack_colors", "gradient_colors", "gradient_angle",
"color", "background_color", "max_value", "scale" }
local function update(graph)
-- Create new empty image
local img = capi.image.argb32(data[graph].width, data[graph].height, nil)
local max_value = data[graph].max_value
local values = data[graph].values
local border_width = 0
if data[graph].border_color then
border_width = 1
end
-- Draw a stacked graph
if data[graph].stack then
if data[graph].scale then
for _, v in ipairs(values) do
for __, sv in ipairs(v) do
if sv > max_value then
max_value = sv
end
end
end
end
-- Draw the background first
img:draw_rectangle(border_width, border_width,
data[graph].width - (2 * border_width),
data[graph].height,
true, data[graph].background_color or "#000000aa")
for i = 0, data[graph].width - (2 * border_width) do
local rel_i = 0
local rel_x = data[graph].width - border_width - i - 1
if data[graph].stack_colors then
for idx, color in ipairs(data[graph].stack_colors) do
local stack_values = values[idx]
if stack_values and i < #stack_values then
local value = stack_values[#stack_values - i] + rel_i
img:draw_line(rel_x, border_width - 1 +
math.ceil((data[graph].height - 2 * border_width) * (1 - (rel_i / max_value))),
rel_x, border_width - 1 +
math.ceil((data[graph].height - 2 * border_width) * (1 - (value / max_value))),
color or "red")
rel_i = value
end
end
end
end
else
if data[graph].scale then
for _, v in ipairs(values) do
if v > max_value then
max_value = v
end
end
end
-- Draw full gradient
if data[graph].gradient_colors then
img:draw_rectangle_gradient(border_width, border_width,
data[graph].width - (2 * border_width),
data[graph].height - (2 * border_width),
data[graph].gradient_colors,
data[graph].gradient_angle or 270)
else
img:draw_rectangle(border_width, border_width,
data[graph].width - (2 * border_width),
data[graph].height - (2 * border_width),
true, data[graph].color or "red")
end
-- Draw the background on no value
if #values ~= 0 then
-- Draw reverse
for i = 0, #values - 1 do
local value = values[#values - i]
if value >= 0 then
value = value / max_value
img:draw_line(data[graph].width - border_width - i - 1,
border_width - 1 +
math.ceil((data[graph].height - 2 * border_width) * (1 - value)),
data[graph].width - border_width - i - 1,
border_width - 1,
data[graph].background_color or "#000000aa")
end
end
end
-- If we didn't draw values in full length, draw a square
-- over the last, left, part to reset everything to 0
if #values < data[graph].width - (2 * border_width) then
img:draw_rectangle(border_width, border_width,
data[graph].width - (2 * border_width) - #values,
data[graph].height - (2 * border_width),
true, data[graph].background_color or "#000000aa")
end
end
-- Draw the border last so that it overlaps already drawn values
if data[graph].border_color then
-- Draw the border
img:draw_rectangle(0, 0, data[graph].width, data[graph].height,
false, data[graph].border_color or "white")
end
-- Update the image
graph.widget.image = img
end
--- Add a value to the graph
-- @param graph The graph.
-- @param value The value between 0 and 1.
-- @param group The stack color group index.
local function add_value(graph, value, group)
if not graph then return end
local value = value or 0
local values = data[graph].values
local max_value = data[graph].max_value
value = math.max(0, value)
if not data[graph].scale then
value = math.min(max_value, value)
end
if data[graph].stack and group then
if not data[graph].values[group]
or type(data[graph].values[group]) ~= "table"
then
data[graph].values[group] = {}
end
values = data[graph].values[group]
end
table.insert(values, value)
local border_width = 0
if data[graph].border then border_width = 2 end
-- Ensure we never have more data than we can draw
while #values > data[graph].width - border_width do
table.remove(values, 1)
end
update(graph)
return graph
end
--- Set the graph height.
-- @param graph The graph.
-- @param height The height to set.
function set_height(graph, height)
if height >= 5 then
data[graph].height = height
update(graph)
end
return graph
end
--- Set the graph width.
-- @param graph The graph.
-- @param width The width to set.
function set_width(graph, width)
if width >= 5 then
data[graph].width = width
update(graph)
end
return graph
end
-- Build properties function
for _, prop in ipairs(properties) do
if not _M["set_" .. prop] then
_M["set_" .. prop] = function(graph, value)
data[graph][prop] = value
update(graph)
return graph
end
end
end
--- Create a graph widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set graph geometry.
-- @return A graph widget.
function new(args)
local args = args or {}
args.type = "imagebox"
local width = args.width or 100
local height = args.height or 20
if width < 5 or height < 5 then return end
local graph = {}
graph.widget = capi.widget(args)
graph.widget.resize = false
data[graph] = { width = width, height = height, values = {}, max_value = 1 }
-- Set methods
graph.add_value = add_value
for _, prop in ipairs(properties) do
graph["set_" .. prop] = _M["set_" .. prop]
end
graph.layout = args.layout or layout.horizontal.leftright
return graph
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,21 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
require("awful.widget.taglist")
require("awful.widget.tasklist")
require("awful.widget.button")
require("awful.widget.launcher")
require("awful.widget.prompt")
require("awful.widget.progressbar")
require("awful.widget.graph")
require("awful.widget.layoutbox")
require("awful.widget.textclock")
require("awful.widget.layout")
--- Widget module for awful
module("awful.widget")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,35 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local util = require("awful.util")
local wbutton = require("awful.widget.button")
local button = require("awful.button")
module("awful.widget.launcher")
--- Create a button widget which will launch a command.
-- @param args Standard widget table arguments, plus image for the image path
-- and command for the command to run on click, or either menu to create menu.
-- @return A launcher widget.
function new(args)
if not args.command and not args.menu then return end
local w = wbutton(args)
if not w then return end
if args.command then
b = util.table.join(w:buttons(), button({}, 1, nil, function () util.spawn(args.command) end))
elseif args.menu then
b = util.table.join(w:buttons(), button({}, 1, nil, function () args.menu:toggle() end))
end
w:buttons(b)
return w
end
setmetatable(_M, { __call = function (_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,58 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @release v3.4.10
-------------------------------------------------
-- Grab environment
local ipairs = ipairs
local type = type
local table = table
local math = math
local setmetatable = setmetatable
local util = require("awful.util")
--- Simple default layout, emulating the fallback C layout
module("awful.widget.layout.default")
local function default(bounds, widgets, screen)
local geometries = {
free = { x = 0, y = 0, width = 0, height = bounds.height }
}
local width = 0
local keys = util.table.keys_filter(widgets, "table", "widget")
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
local nbounds = util.table.clone(bounds)
local g = layout(nbounds, v, screen)
for _, w in ipairs(g) do
table.insert(geometries, w)
end
else
if v.visible then
local e = v:extents(screen)
e.x = 0
e.y = 0
e.width = math.min(e.width, bounds.width)
e.height = bounds.height
width = math.max(e.width, width)
table.insert(geometries, e)
else
table.insert(geometries, { x = 0, y = 0, width = 0, height = 0 })
end
end
end
geometries.free.width = bounds.width - width
geometries.free.x = width
return geometries
end
setmetatable(_M, { __call = function(_, ...) return default(...) end })

View File

@ -0,0 +1,188 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @release v3.4.10
-------------------------------------------------
-- Grab environment
local ipairs = ipairs
local type = type
local table = table
local math = math
local util = require("awful.util")
local default = require("awful.widget.layout.default")
local margins = awful.widget.layout.margins
--- Horizontal widget layout
module("awful.widget.layout.horizontal")
local function horizontal(direction, bounds, widgets, screen)
local geometries = { }
local x = 0
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
if margins[v] then
bounds.width = bounds.width - (margins[v].left or 0) - (margins[v].right or 0)
bounds.height = bounds.height - (margins[v].top or 0) - (margins[v].bottom or 0)
end
local g = layout(bounds, v, screen)
if margins[v] then
x = x + (margins[v].left or 0)
end
for _, v in ipairs(g) do
v.x = v.x + x
v.y = v.y + (margins[v] and (margins[v].top and margins[v].top or 0) or 0)
table.insert(geometries, v)
end
bounds = g.free
if margins[v] then
x = x + g.free.x + (margins[v].right or 0)
bounds.width = bounds.width - (margins[v].right or 0) - (margins[v].left or 0)
else
x = x + g.free.x
end
elseif type(v) == "widget" then
local g
if v.visible then
g = v:extents(screen)
if margins[v] then
g.width = g.width + (margins[v].left or 0) + (margins[v].right or 0)
g.height = g.height + (margins[v].top or 0) + (margins[v].bottom or 0)
end
else
g = {
width = 0,
height = 0,
}
end
if v.resize and g.width > 0 and g.height > 0 then
local ratio = g.width / g.height
g.width = math.floor(bounds.height * ratio)
g.height = bounds.height
end
if g.width > bounds.width then
g.width = bounds.width
end
g.height = bounds.height
if margins[v] then
g.y = (margins[v].top or 0)
else
g.y = 0
end
if direction == "leftright" then
if margins[v] then
g.x = x + (margins[v].left or 0)
else
g.x = x
end
x = x + g.width
else
if margins[v] then
g.x = x + bounds.width - g.width + (margins[v].left or 0)
else
g.x = x + bounds.width - g.width
end
end
bounds.width = bounds.width - g.width
table.insert(geometries, g)
end
end
geometries.free = util.table.clone(bounds)
geometries.free.x = x
geometries.free.y = 0
return geometries
end
function flex(bounds, widgets, screen)
local geometries = {
free = util.table.clone(bounds)
}
-- the flex layout always uses the complete available place, thus we return
-- no usable free area
geometries.free.width = 0
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
local nelements = 0
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
nelements = nelements + 1
elseif type(v) == "widget" then
local g = v:extents()
if v.resize and g.width > 0 and g.height > 0 then
bounds.width = bounds.width - bounds.height
elseif g.width > 0 and g.height > 0 then
nelements = nelements + 1
end
end
end
nelements = (nelements == 0) and 1 or nelements
local x = 0
local width = bounds.width / nelements
for _, k in ipairs(util.table.keys(widgets)) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
local g = layout(bounds, v, screen)
for _, v in ipairs(g) do
v.x = v.x + x
table.insert(geometries, v)
end
bounds = g.free
elseif type(v) == "widget" then
local g = v:extents(screen)
g.resize = v.resize
if v.resize and g.width > 0 and g.height > 0 then
g.width = bounds.height
g.height = bounds.height
g.x = x
g.y = bounds.y
x = x + g.width
elseif g.width > 0 and g.height > 0 then
g.x = x
g.y = bounds.y
g.width = math.floor(width + 0.5)
g.height = bounds.height
x = x + width
else
g.x = 0
g.y = 0
g.width = 0
g.height = 0
end
table.insert(geometries, g)
end
end
return geometries
end
function leftright(...)
return horizontal("leftright", ...)
end
function rightleft(...)
return horizontal("rightleft", ...)
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,23 @@
local setmetatable = setmetatable
local require = require
-- Widget layouts
module("awful.widget.layout")
--- Widgets margins.
-- <p>In this table you can set the margin you want the layout to use when
-- positionning your widgets.
-- For example, if you want to put 10 pixel free on left on a widget, add this:
-- <code>
-- awful.widget.layout.margins[mywidget] = { left = 10 }
-- </code>
-- </p>
-- @name margins
-- @class table
margins = setmetatable({}, { __mode = 'k' })
require("awful.widget.layout.horizontal")
require("awful.widget.layout.vertical")
require("awful.widget.layout.default")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,101 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @release v3.4.10
-------------------------------------------------
-- Grab environment
local ipairs = ipairs
local type = type
local table = table
local math = math
local util = require("awful.util")
local default = require("awful.widget.layout.default")
--- Vertical widget layout
module("awful.widget.layout.vertical")
function flex(bounds, widgets, screen)
local geometries = {
free = util.table.clone(bounds)
}
local y = 0
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
local nelements = 0
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
nelements = nelements + 1
else
local e = v:extents()
if v.visible and e.width > 0 and e.height > 0 then
nelements = nelements + 1
end
end
end
if nelements == 0 then return geometries end
local height = math.floor(bounds.height / nelements)
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
-- we need to modify the height a bit because vertical layouts always span the
-- whole height
nbounds = util.table.clone(bounds)
nbounds.height = height
local g = layout(nbounds, v, screen)
for _, w in ipairs(g) do
w.y = w.y + y
table.insert(geometries, w)
end
y = y + height
elseif type(v) == "widget" then
local g
if v.visible then
g = v:extents(screen)
else
g = {
["width"] = 0,
["height"] = 0
}
end
g.ratio = 1
if g.height > 0 and g.width > 0 then
g.ratio = g.width / g.height
end
g.height = height
if v.resize then
g.width = g.height * g.ratio
end
g.width = math.min(g.width, bounds.width)
geometries.free.x = math.max(geometries.free.x, g.width)
g.x = 0
g.y = y
y = y + g.height
bounds.height = bounds.height - g.height
table.insert(geometries, g)
end
end
local maxw = 0
local maxx = 0
for _, v in ipairs(geometries) do
if v.width > maxw then maxw = v.width end
if v.x > maxx then maxx = v.x end
end
geometries.free.width = geometries.free.width - maxw
geometries.free.x = geometries.free.x + maxw
geometries.free.height = nelements * height
geometries.free.y = 0
return geometries
end

View File

@ -0,0 +1,53 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local ipairs = ipairs
local button = require("awful.button")
local layout = require("awful.layout")
local tag = require("awful.tag")
local beautiful = require("beautiful")
local capi = { image = image,
screen = screen,
widget = widget }
--- Layoutbox widget.
module("awful.widget.layoutbox")
local function update(w, screen)
local layout = layout.getname(layout.get(screen))
if layout and beautiful["layout_" ..layout] then
w.image = capi.image(beautiful["layout_" ..layout])
else
w.image = nil
end
end
--- Create a layoutbox widget. It draws a picture with the current layout
-- symbol of the current tag.
-- @param screen The screen number that the layout will be represented for.
-- @param args Standard arguments for an imagebox widget.
-- @return An imagebox widget configured as a layoutbox.
function new(screen, args)
local screen = screen or 1
local args = args or {}
args.type = "imagebox"
local w = capi.widget(args)
update(w, screen)
local function update_on_tag_selection(tag)
return update(w, tag.screen)
end
tag.attached_add_signal(screen, "property::selected", update_on_tag_selection)
tag.attached_add_signal(screen, "property::layout", update_on_tag_selection)
return w
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,243 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local ipairs = ipairs
local math = math
local capi = { image = image,
widget = widget }
local layout = require("awful.widget.layout")
--- A progressbar widget.
module("awful.widget.progressbar")
local data = setmetatable({}, { __mode = "k" })
--- Set the progressbar border color.
-- If the value is nil, no border will be drawn.
-- @name set_border_color
-- @class function
-- @param progressbar The progressbar.
-- @param color The border color to set.
--- Set the progressbar foreground color as a gradient.
-- @name set_gradient_colors
-- @class function
-- @param progressbar The progressbar.
-- @param gradient_colors A table with gradients colors. The distance between each color
-- can also be specified. Example: { "red", "blue" } or { "red", "green",
-- "blue", blue = 10 } to specify blue distance from other colors.
--- Set the progressbar foreground color.
-- @name set_color
-- @class function
-- @param progressbar The progressbar.
-- @param color The progressbar color.
--- Set the progressbar background color.
-- @name set_background_color
-- @class function
-- @param progressbar The progressbar.
-- @param color The progressbar background color.
--- Set the progressbar to draw vertically. Default is false.
-- @name set_vertical
-- @class function
-- @param progressbar The progressbar.
-- @param vertical A boolean value.
--- Set the progressbar to draw ticks. Default is false.
-- @name set_ticks
-- @class function
-- @param progressbar The progressbar.
-- @param ticks A boolean value.
--- Set the progressbar ticks gap.
-- @name set_ticks_gap
-- @class function
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the progressbar ticks size.
-- @name set_ticks_size
-- @class function
-- @param progressbar The progressbar.
-- @param value The value.
--- Set the maximum value the progressbar should handle.
-- @name set_max_value
-- @class function
-- @param progressbar The progressbar.
-- @param value The value.
local properties = { "width", "height", "border_color",
"gradient_colors", "color", "background_color",
"vertical", "value", "max_value",
"ticks", "ticks_gap", "ticks_size" }
local function update(pbar)
local width = data[pbar].width or 100
local height = data[pbar].height or 20
local ticks_gap = data[pbar].ticks_gap or 1
local ticks_size = data[pbar].ticks_size or 4
-- Create new empty image
local img = capi.image.argb32(width, height, nil)
local value = data[pbar].value
local max_value = data[pbar].max_value
if value >= 0 then
value = value / max_value
end
local over_drawn_width = width
local over_drawn_height = height
local border_width = 0
if data[pbar].border_color then
-- Draw border
img:draw_rectangle(0, 0, width, height, false, data[pbar].border_color)
over_drawn_width = width - 2 -- remove 2 for borders
over_drawn_height = height - 2 -- remove 2 for borders
border_width = 1
end
local angle = 270
if data[pbar].vertical then
angle = 180
end
-- Draw full gradient
if data[pbar].gradient_colors then
img:draw_rectangle_gradient(border_width, border_width,
over_drawn_width, over_drawn_height,
data[pbar].gradient_colors, angle)
else
img:draw_rectangle(border_width, border_width,
over_drawn_width, over_drawn_height,
true, data[pbar].color or "red")
end
-- Cover the part that is not set with a rectangle
if data[pbar].vertical then
local rel_height = math.floor(over_drawn_height * (1 - value))
img:draw_rectangle(border_width,
border_width,
over_drawn_width,
rel_height,
true, data[pbar].background_color or "#000000aa")
-- Place smaller pieces over the gradient if ticks are enabled
if data[pbar].ticks then
for i=0, height / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_height / 1 - (ticks_size+ticks_gap) * i
if rel_offset >= rel_height then
img:draw_rectangle(border_width,
rel_offset,
over_drawn_width,
ticks_gap,
true, data[pbar].background_color or "#000000aa")
end
end
end
else
local rel_x = math.ceil(over_drawn_width * value)
img:draw_rectangle(border_width + rel_x,
border_width,
over_drawn_width - rel_x,
over_drawn_height,
true, data[pbar].background_color or "#000000aa")
if data[pbar].ticks then
for i=0, width / (ticks_size+ticks_gap)-border_width do
local rel_offset = over_drawn_width / 1 - (ticks_size+ticks_gap) * i
if rel_offset <= rel_x then
img:draw_rectangle(rel_offset,
border_width,
ticks_gap,
over_drawn_height,
true, data[pbar].background_color or "#000000aa")
end
end
end
end
-- Update the image
pbar.widget.image = img
end
--- Set the progressbar value.
-- @param pbar The progress bar.
-- @param value The progress bar value between 0 and 1.
function set_value(pbar, value)
local value = value or 0
local max_value = data[pbar].max_value
data[pbar].value = math.min(max_value, math.max(0, value))
update(pbar)
return pbar
end
--- Set the progressbar height.
-- @param progressbar The progressbar.
-- @param height The height to set.
function set_height(progressbar, height)
data[progressbar].height = height
update(progressbar)
return progressbar
end
--- Set the progressbar width.
-- @param progressbar The progressbar.
-- @param width The width to set.
function set_width(progressbar, width)
data[progressbar].width = width
update(progressbar)
return progressbar
end
-- Build properties function
for _, prop in ipairs(properties) do
if not _M["set_" .. prop] then
_M["set_" .. prop] = function(pbar, value)
data[pbar][prop] = value
update(pbar)
return pbar
end
end
end
--- Create a progressbar widget.
-- @param args Standard widget() arguments. You should add width and height
-- key to set progressbar geometry.
-- @return A progressbar widget.
function new(args)
local args = args or {}
local width = args.width or 100
local height = args.height or 20
args.type = "imagebox"
local pbar = {}
pbar.widget = capi.widget(args)
pbar.widget.resize = false
data[pbar] = { width = width, height = height, value = 0, max_value = 1 }
-- Set methods
for _, prop in ipairs(properties) do
pbar["set_" .. prop] = _M["set_" .. prop]
end
pbar.layout = args.layout or layout.horizontal.leftright
return pbar
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,51 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local capi = { widget = widget }
local completion = require("awful.completion")
local util = require("awful.util")
local prompt = require("awful.prompt")
local layout = require("awful.widget.layout")
local type = type
module("awful.widget.prompt")
--- Run method for promptbox.
-- @param promptbox The promptbox to run.
local function run(promptbox)
return prompt.run({ prompt = promptbox.prompt },
promptbox.widget,
function (...)
local result = util.spawn(...)
if type(result) == "string" then
promptbox.widget.text = result
end
end,
completion.shell,
util.getdir("cache") .. "/history")
end
--- Create a prompt widget which will launch a command.
-- @param args Standard widget table arguments, with prompt to change the
-- default prompt.
-- @return A launcher widget.
function new(args)
local args = args or {}
local promptbox = {}
args.type = "textbox"
promptbox.widget = capi.widget(args)
promptbox.widget.ellipsize = "start"
promptbox.run = run
promptbox.prompt = args.prompt or "Run: "
promptbox.layout = args.layout or layout.horizontal.leftright
return promptbox
end
setmetatable(_M, { __call = function (_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,196 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local capi = { widget = widget,
screen = screen,
image = image,
client = client }
local type = type
local setmetatable = setmetatable
local pairs = pairs
local ipairs = ipairs
local table = table
local common = require("awful.widget.common")
local util = require("awful.util")
local tag = require("awful.tag")
local beautiful = require("beautiful")
local layout = require("awful.widget.layout")
--- Taglist widget module for awful
module("awful.widget.taglist")
label = {}
local function taglist_update (screen, w, label, buttons, data, widgets)
local tags = capi.screen[screen]:tags()
local showntags = {}
for k, t in ipairs(tags) do
if not tag.getproperty(t, "hide") then
table.insert(showntags, t)
end
end
common.list_update(w, buttons, label, data, widgets, showntags)
end
--- Get the tag object the given widget appears on.
-- @param widget The widget the look for.
-- @return The tag object.
function gettag(widget)
return common.tagwidgets[widget]
end
--- Create a new taglist widget.
-- @param screen The screen to draw tag list for.
-- @param label Label function to use.
-- @param buttons A table with buttons binding to set.
function new(screen, label, buttons)
local w = {
layout = layout.horizontal.leftright
}
local widgets = { }
widgets.imagebox = { }
widgets.textbox = { ["margin"] = { ["left"] = 0,
["right"] = 0},
["bg_resize"] = true
}
local data = setmetatable({}, { __mode = 'kv' })
local u = function (s)
if s == screen then
taglist_update(s, w, label, buttons, data, widgets)
end
end
local uc = function (c) return u(c.screen) end
capi.client.add_signal("focus", uc)
capi.client.add_signal("unfocus", uc)
tag.attached_add_signal(screen, "property::selected", uc)
tag.attached_add_signal(screen, "property::icon", uc)
tag.attached_add_signal(screen, "property::hide", uc)
tag.attached_add_signal(screen, "property::name", uc)
capi.screen[screen]:add_signal("tag::attach", function(screen, tag)
u(screen.index)
end)
capi.screen[screen]:add_signal("tag::detach", function(screen, tag)
u(screen.index)
end)
capi.client.add_signal("new", function(c)
c:add_signal("property::urgent", uc)
c:add_signal("property::screen", function(c)
-- If client change screen, refresh it anyway since we don't from
-- which screen it was coming :-)
u(screen)
end)
c:add_signal("tagged", uc)
c:add_signal("untagged", uc)
end)
capi.client.add_signal("unmanage", uc)
u(screen)
return w
end
--- Return labels for a taglist widget with all tag from screen.
-- It returns the tag name and set a special
-- foreground and background color for selected tags.
-- @param t The tag.
-- @param args The arguments table.
-- bg_focus The background color for selected tag.
-- fg_focus The foreground color for selected tag.
-- bg_urgent The background color for urgent tags.
-- fg_urgent The foreground color for urgent tags.
-- squares_sel Optional: a user provided image for selected squares.
-- squares_unsel Optional: a user provided image for unselected squares.
-- squares_resize Optional: true or false to resize squares.
-- @return A string to print, a background color, a background image and a
-- background resize value.
function label.all(t, args)
if not args then args = {} end
local theme = beautiful.get()
local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
local taglist_squares_sel = args.squares_sel or theme.taglist_squares_sel
local taglist_squares_unsel = args.squares_unsel or theme.taglist_squares_unsel
local taglist_squares_resize = theme.taglist_squares_resize or args.squares_resize or "true"
local font = args.font or theme.taglist_font or theme.font or ""
local text = "<span font_desc='"..font.."'>"
local sel = capi.client.focus
local bg_color = nil
local fg_color = nil
local bg_image
local icon
local bg_resize = false
local is_selected = false
if t.selected then
bg_color = bg_focus
fg_color = fg_focus
end
if sel then
if taglist_squares_sel then
-- Check that the selected clients is tagged with 't'.
local seltags = sel:tags()
for _, v in ipairs(seltags) do
if v == t then
bg_image = capi.image(taglist_squares_sel)
bg_resize = taglist_squares_resize == "true"
is_selected = true
break
end
end
end
end
if not is_selected then
local cls = t:clients()
if #cls > 0 and taglist_squares_unsel then
bg_image = capi.image(taglist_squares_unsel)
bg_resize = taglist_squares_resize == "true"
end
for k, c in pairs(cls) do
if c.urgent then
if bg_urgent then bg_color = bg_urgent end
if fg_urgent then fg_color = fg_urgent end
break
end
end
end
if not tag.getproperty(t, "icon_only") then
if fg_color then
text = text .. "<span color='"..util.color_strip_alpha(fg_color).."'>"
text = " " .. text.. (util.escape(t.name) or "") .." </span>"
else
text = text .. " " .. (util.escape(t.name) or "") .. " "
end
end
text = text .. "</span>"
if tag.geticon(t) and type(tag.geticon(t)) == "image" then
icon = tag.geticon(t)
elseif tag.geticon(t) then
icon = capi.image(tag.geticon(t))
end
return text, bg_color, bg_image, icon
end
--- Return labels for a taglist widget with all *non empty* tags from screen.
-- It returns the tag name and set a special
-- foreground and background color for selected tags.
-- @param t The tag.
-- @param args The arguments table.
-- bg_focus The background color for selected tag.
-- fg_focus The foreground color for selected tag.
-- bg_urgent The background color for urgent tags.
-- fg_urgent The foreground color for urgent tags.
-- @return A string to print, a background color, a background image and a
-- background resize value.
function label.noempty(t, args)
if #t:clients() > 0 or t.selected then
return label.all(t, args)
end
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,213 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
-- Grab environment we need
local capi = { screen = screen,
image = image,
client = client }
local ipairs = ipairs
local type = type
local setmetatable = setmetatable
local table = table
local common = require("awful.widget.common")
local beautiful = require("beautiful")
local client = require("awful.client")
local util = require("awful.util")
local tag = require("awful.tag")
local layout = require("awful.widget.layout")
--- Tasklist widget module for awful
module("awful.widget.tasklist")
-- Public structures
label = {}
local function tasklist_update(w, buttons, label, data, widgets)
local clients = capi.client.get()
local shownclients = {}
for k, c in ipairs(clients) do
if not (c.skip_taskbar or c.hidden
or c.type == "splash" or c.type == "dock" or c.type == "desktop") then
table.insert(shownclients, c)
end
end
clients = shownclients
common.list_update(w, buttons, label, data, widgets, clients)
end
--- Create a new tasklist widget.
-- @param label Label function to use.
-- @param buttons A table with buttons binding to set.
function new(label, buttons)
local w = {
layout = layout.horizontal.flex
}
local widgets = { }
widgets.imagebox = { }
widgets.textbox = { margin = { left = 2,
right = 2 },
bg_resize = true,
bg_align = "right"
}
local data = setmetatable({}, { __mode = 'kv' })
local u = function () tasklist_update(w, buttons, label, data, widgets) end
for s = 1, capi.screen.count() do
tag.attached_add_signal(s, "property::selected", u)
capi.screen[s]:add_signal("tag::attach", u)
capi.screen[s]:add_signal("tag::detach", u)
end
capi.client.add_signal("new", function (c)
c:add_signal("property::urgent", u)
c:add_signal("property::floating", u)
c:add_signal("property::maximized_horizontal", u)
c:add_signal("property::maximized_vertical", u)
c:add_signal("property::minimized", u)
c:add_signal("property::name", u)
c:add_signal("property::icon_name", u)
c:add_signal("property::icon", u)
c:add_signal("property::skip_taskbar", u)
c:add_signal("property::hidden", u)
c:add_signal("tagged", u)
c:add_signal("untagged", u)
end)
capi.client.add_signal("unmanage", u)
capi.client.add_signal("list", u)
capi.client.add_signal("focus", u)
capi.client.add_signal("unfocus", u)
u()
return w
end
local function widget_tasklist_label_common(c, args)
if not args then args = {} end
local theme = beautiful.get()
local fg_focus = args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus
local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus
local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
local fg_minimize = args.fg_minimize or theme.tasklist_fg_minimize or theme.fg_minimize
local bg_minimize = args.bg_minimize or theme.tasklist_bg_minimize or theme.bg_minimize
local floating_icon = args.floating_icon or theme.tasklist_floating_icon
local font = args.font or theme.tasklist_font or theme.font or ""
local bg = nil
local text = "<span font_desc='"..font.."'>"
local name
local status_image
if client.floating.get(c) and floating_icon then
status_image = capi.image(floating_icon)
end
if c.minimized then
name = util.escape(c.icon_name) or util.escape(c.name) or util.escape("<untitled>")
else
name = util.escape(c.name) or util.escape("<untitled>")
end
if capi.client.focus == c then
bg = bg_focus
if fg_focus then
text = text .. "<span color='"..util.color_strip_alpha(fg_focus).."'>"..name.."</span>"
else
text = text .. name
end
elseif c.urgent and fg_urgent then
bg = bg_urgent
text = text .. "<span color='"..util.color_strip_alpha(fg_urgent).."'>"..name.."</span>"
elseif c.minimized and fg_minimize and bg_minimize then
bg = bg_minimize
text = text .. "<span color='"..util.color_strip_alpha(fg_minimize).."'>"..name.."</span>"
else
text = text .. name
end
text = text .. "</span>"
-- return text, bg, status_image, c.icon
return text, bg, status_image, nil
end
--- Return labels for a tasklist widget with clients from all tags and screen.
-- It returns the client name and set a special
-- foreground and background color for focused client.
-- It also puts a special icon for floating windows.
-- @param c The client.
-- @param screen The screen we are drawing on.
-- @param args The arguments table.
-- bg_focus The background color for focused client.
-- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients.
-- @return A string to print, a background color and a status image.
function label.allscreen(c, screen, args)
return widget_tasklist_label_common(c, args)
end
--- Return labels for a tasklist widget with clients from all tags.
-- It returns the client name and set a special
-- foreground and background color for focused client.
-- It also puts a special icon for floating windows.
-- @param c The client.
-- @param screen The screen we are drawing on.
-- @param args The arguments table.
-- bg_focus The background color for focused client.
-- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients.
-- @return A string to print, a background color and a status image.
function label.alltags(c, screen, args)
-- Only print client on the same screen as this widget
if c.screen ~= screen then return end
return widget_tasklist_label_common(c, args)
end
--- Return labels for a tasklist widget with clients from currently selected tags.
-- It returns the client name and set a special
-- foreground and background color for focused client.
-- It also puts a special icon for floating windows.
-- @param c The client.
-- @param screen The screen we are drawing on.
-- @param args The arguments table.
-- bg_focus The background color for focused client.
-- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients.
-- @return A string to print, a background color and a status image.
function label.currenttags(c, screen, args)
-- Only print client on the same screen as this widget
if c.screen ~= screen then return end
-- Include sticky client too
if c.sticky then return widget_tasklist_label_common(c, args) end
for k, t in ipairs(capi.screen[screen]:tags()) do
if t.selected then
local ctags = c:tags()
for _, v in ipairs(ctags) do
if v == t then
return widget_tasklist_label_common(c, args)
end
end
end
end
end
--- Return label for only the currently focused client.
-- It returns the client name and set a special
-- foreground and background color for focused client.
-- It also puts a special icon for floating windows.
-- @param c The client.
-- @param screen The screen we are drawing on.
-- @param args The arguments table.
-- bg_focus The background color for focused client.
-- fg_focus The foreground color for focused client.
-- bg_urgent The background color for urgent clients.
-- fg_urgent The foreground color for urgent clients.
-- @return A string to print, a background color and a status image.
function label.focused(c, screen, args)
-- Only print client on the same screen as this widget
if c.screen == screen and capi.client.focus == c then
return widget_tasklist_label_common(c, args)
end
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,35 @@
---------------------------------------------------------------------------
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2009 Julien Danjou
-- @release v3.4.10
---------------------------------------------------------------------------
local setmetatable = setmetatable
local os = os
local capi = { widget = widget,
timer = timer }
--- Text clock widget.
module("awful.widget.textclock")
--- Create a textclock widget. It draws the time it is in a textbox.
-- @param args Standard arguments for textbox widget.
-- @param format The time format. Default is " %a %b %d, %H:%M ".
-- @param timeout How often update the time. Default is 60.
-- @return A textbox widget.
function new(args, format, timeout)
local args = args or {}
local format = format or " %a %b %d, %H:%M "
local timeout = timeout or 60
args.type = "textbox"
local w = capi.widget(args)
local timer = capi.timer { timeout = timeout }
w.text = os.date(format)
timer:add_signal("timeout", function() w.text = os.date(format) end)
timer:start()
return w
end
setmetatable(_M, { __call = function(_, ...) return new(...) end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,76 @@
----------------------------------------------------------------------------
-- @author Damien Leone &lt;damien.leone@gmail.com&gt;
-- @author Julien Danjou &lt;julien@danjou.info&gt;
-- @copyright 2008-2009 Damien Leone, Julien Danjou
-- @release v3.4.9
----------------------------------------------------------------------------
-- Grab environment
local io = io
local os = os
local print = print
local pcall = pcall
local pairs = pairs
local type = type
local dofile = dofile
local setmetatable = setmetatable
local util = require("awful.util")
local package = package
local capi =
{
screen = screen,
awesome = awesome,
image = image
}
--- Theme library.
module("beautiful")
-- Local data
local theme
--- Init function, should be runned at the beginning of configuration file.
-- @param path The theme file path.
function init(path)
if path then
local success
success, theme = pcall(function() return dofile(path) end)
if not success then
return print("E: beautiful: error loading theme file " .. theme)
elseif theme then
-- try and grab user's $HOME directory
local homedir = os.getenv("HOME")
-- expand '~'
if homedir then
for k, v in pairs(theme) do
if type(v) == "string" then theme[k] = v:gsub("~", homedir) end
end
end
-- setup wallpaper
if theme.wallpaper_cmd then
for s = 1, capi.screen.count() do
util.spawn(theme.wallpaper_cmd[util.cycle(#theme.wallpaper_cmd, s)], false, s)
end
end
if theme.font then capi.awesome.font = theme.font end
if theme.fg_normal then capi.awesome.fg = theme.fg_normal end
if theme.bg_normal then capi.awesome.bg = theme.bg_normal end
else
return print("E: beautiful: error loading theme file " .. path)
end
else
return print("E: beautiful: error loading theme: no path specified")
end
end
--- Get the current theme.
-- @return The current theme table.
function get()
return theme
end
setmetatable(_M, { __index = function(t, k) return theme[k] end })
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,121 @@
-- original code made by Bzed and published on http://awesome.naquadah.org/wiki/Calendar_widget
-- modified by Marc Dequènes (Duck) <Duck@DuckCorp.org> (2009-12-29), under the same licence,
-- and with the following changes:
-- + transformed to module
-- + the current day formating is customizable
-- modified by Jörg Thalheim (Mic92) <jthalheim@gmail.com> (2011), under the same licence,
-- and with the following changes:
-- + use tooltip instead of naughty.notify
-- + rename it to cal
--
-- # How to Install #
-- 1. Download the code and move it into your config directory
-- wget --no-check-certificate https://github.com/Mic92/awesome-dotfiles/raw/master/cal.lua -O $XDG_CONFIG_HOME/awesome/cal.lua
-- 2. require it in your rc.lua
-- require("cal")
-- 3. attach the calendar to a widget of your choice (ex mytextclock)
-- cal.register(mytextclock)
-- If you don't like the default current day formating you can change it as following
-- cal.register(mytextclock, "<b>%s</b>") -- now the current day is bold instead of underlined
--
-- # How to Use #
-- Just hover with your mouse over the widget, you register and the calendar popup.
-- On clicking or by using the mouse wheel the displayed month changes.
-- Pressing Shift + Mouse click change the year.
local string = {format = string.format}
local os = {date = os.date, time = os.time}
local awful = require("awful")
module("cal")
local tooltip
local state = {}
local current_day_format = "<u>%s</u>"
function displayMonth(month,year,weekStart)
local t,wkSt=os.time{year=year, month=month+1, day=0},weekStart or 1
local d=os.date("*t",t)
local mthDays,stDay=d.day,(d.wday-d.day-wkSt+1)%7
local lines = " "
for x=0,6 do
lines = lines .. os.date("%a ",os.time{year=2006,month=1,day=x+wkSt})
end
lines = lines .. "\n" .. os.date(" %V",os.time{year=year,month=month,day=1})
local writeLine = 1
while writeLine < (stDay + 1) do
lines = lines .. " "
writeLine = writeLine + 1
end
for d=1,mthDays do
local x = d
local t = os.time{year=year,month=month,day=d}
if writeLine == 8 then
writeLine = 1
lines = lines .. "\n" .. os.date(" %V",t)
end
if os.date("%Y-%m-%d") == os.date("%Y-%m-%d", t) then
x = string.format(current_day_format, d)
end
if d < 10 then
x = " " .. x
end
lines = lines .. " " .. x
writeLine = writeLine + 1
end
local header = os.date("%B %Y\n",os.time{year=year,month=month,day=1})
return header .. "\n" .. lines
end
function register(mywidget, custom_current_day_format)
if custom_current_day_format then current_day_format = custom_current_day_format end
if not tooltip then
tooltip = awful.tooltip({})
end
tooltip:add_to_object(mywidget)
mywidget:add_signal("mouse::enter", function()
local month, year = os.date('%m'), os.date('%Y')
state = {month, year}
tooltip:set_text(string.format('<span font_desc="monospace">%s</span>', displayMonth(month, year, 2)))
end)
mywidget:buttons(awful.util.table.join(
awful.button({ }, 1, function()
switchMonth(-1)
end),
awful.button({ }, 3, function()
switchMonth(1)
end),
awful.button({ }, 4, function()
switchMonth(-1)
end),
awful.button({ }, 5, function()
switchMonth(1)
end),
awful.button({ 'Shift' }, 1, function()
switchMonth(-12)
end),
awful.button({ 'Shift' }, 3, function()
switchMonth(12)
end),
awful.button({ 'Shift' }, 4, function()
switchMonth(-12)
end),
awful.button({ 'Shift' }, 5, function()
switchMonth(12)
end)))
end
function switchMonth(delta)
state[1] = state[1] + (delta or 1)
local text = string.format('<span font_desc="monospace">%s</span>', displayMonth(state[1], state[2], 2))
tooltip:set_text(text)
end

View File

@ -0,0 +1,594 @@
----------------------------------------------------------------------------
-- @author koniu &lt;gkusnierz@gmail.com&gt;
-- @copyright 2008 koniu
-- @release v3.4.9
----------------------------------------------------------------------------
-- Package environment
local pairs = pairs
local table = table
local type = type
local string = string
local pcall = pcall
local capi = { screen = screen,
awesome = awesome,
dbus = dbus,
widget = widget,
wibox = wibox,
image = image,
timer = timer }
local button = require("awful.button")
local util = require("awful.util")
local bt = require("beautiful")
local layout = require("awful.widget.layout")
--- Notification library
module("naughty")
--- Naughty configuration - a table containing common popup settings.
-- @name config
-- @field padding Space between popups and edge of the workarea. Default: 4
-- @field spacing Spacing between popups. Default: 1
-- @field icon_dirs List of directories that will be checked by getIcon()
-- Default: { "/usr/share/pixmaps/", }
-- @field icon_formats List of formats that will be checked by getIcon()
-- Default: { "png", "gif" }
-- @field default_preset Preset to be used by default.
-- Default: config.presets.normal
-- @class table
config = {}
config.padding = 4
config.spacing = 1
config.icon_dirs = { "/usr/share/pixmaps/", }
config.icon_formats = { "png", "gif" }
--- Notification Presets - a table containing presets for different purposes
-- Preset is a table of any parameters available to notify()
-- You have to pass a reference of a preset in your notify() call to use the preset
-- At least the default preset named "normal" has to be defined
-- The presets "low", "normal" and "critical" are used for notifications over DBUS
-- @name config.presets
-- @field low The preset for notifications with low urgency level
-- @field normal The default preset for every notification without a preset that will also be used for normal urgency level
-- @field critical The preset for notifications with a critical urgency level
-- @class table
config.presets = {
normal = {},
low = {
timeout = 5
},
critical = {
bg = "#ff0000",
fg = "#ffffff",
timeout = 0,
}
}
config.default_preset = config.presets.normal
-- DBUS Notification constants
urgency = {
low = "\0",
normal = "\1",
critical = "\2"
}
--- DBUS notification to preset mapping
-- @name config.mapping
-- The first element is an object containing the filter
-- If the rules in the filter matches the associated preset will be applied
-- The rules object can contain: urgency, category, appname
-- The second element is the preset
config.mapping = {
{{urgency = urgency.low}, config.presets.low},
{{urgency = urgency.normal}, config.presets.normal},
{{urgency = urgency.critical}, config.presets.critical}
}
-- Counter for the notifications
-- Required for later access via DBUS
local counter = 1
-- True if notifying is suspended
local suspended = false
--- Index of notifications. See config table for valid 'position' values.
-- Each element is a table consisting of:
-- @field box Wibox object containing the popup
-- @field height Popup height
-- @field width Popup width
-- @field die Function to be executed on timeout
-- @field id Unique notification id based on a counter
-- @name notifications[screen][position]
-- @class table
notifications = { suspended = { } }
for s = 1, capi.screen.count() do
notifications[s] = {
top_left = {},
top_right = {},
bottom_left = {},
bottom_right = {},
}
end
--- Suspend notifications
function suspend()
suspended = true
end
--- Resume notifications
function resume()
suspended = false
for i, v in pairs(notifications.suspended) do
v.box.visible = true
if v.timer then v.timer:start() end
end
notifications.suspended = { }
end
-- Evaluate desired position of the notification by index - internal
-- @param idx Index of the notification
-- @param position top_right | top_left | bottom_right | bottom_left
-- @param height Popup height
-- @param width Popup width (optional)
-- @return Absolute position and index in { x = X, y = Y, idx = I } table
local function get_offset(screen, position, idx, width, height)
local ws = capi.screen[screen].workarea
local v = {}
local idx = idx or #notifications[screen][position] + 1
local width = width or notifications[screen][position][idx].width
-- calculate x
if position:match("left") then
v.x = ws.x + config.padding
else
v.x = ws.x + ws.width - (width + config.padding)
end
-- calculate existing popups' height
local existing = 0
for i = 1, idx-1, 1 do
existing = existing + notifications[screen][position][i].height + config.spacing
end
-- calculate y
if position:match("top") then
v.y = ws.y + config.padding + existing
else
v.y = ws.y + ws.height - (config.padding + height + existing)
end
-- if positioned outside workarea, destroy oldest popup and recalculate
if v.y + height > ws.y + ws.height or v.y < ws.y then
idx = idx - 1
destroy(notifications[screen][position][1])
v = get_offset(screen, position, idx, width, height)
end
if not v.idx then v.idx = idx end
return v
end
-- Re-arrange notifications according to their position and index - internal
-- @return None
local function arrange(screen)
for p,pos in pairs(notifications[screen]) do
for i,notification in pairs(notifications[screen][p]) do
local offset = get_offset(screen, p, i, notification.width, notification.height)
notification.box:geometry({ x = offset.x, y = offset.y })
notification.idx = offset.idx
end
end
end
--- Destroy notification by notification object
-- @param notification Notification object to be destroyed
-- @return True if the popup was successfully destroyed, nil otherwise
function destroy(notification)
if notification and notification.box.screen then
if suspended then
for k, v in pairs(notifications.suspended) do
if v.box == notification.box then
table.remove(notifications.suspended, k)
break
end
end
end
local scr = notification.box.screen
table.remove(notifications[notification.box.screen][notification.position], notification.idx)
if notification.timer then
notification.timer:stop()
end
notification.box.screen = nil
arrange(scr)
return true
end
end
-- Get notification by ID
-- @param id ID of the notification
-- @return notification object if it was found, nil otherwise
local function getById(id)
-- iterate the notifications to get the notfications with the correct ID
for s = 1, capi.screen.count() do
for p,pos in pairs(notifications[s]) do
for i,notification in pairs(notifications[s][p]) do
if notification.id == id then
return notification
end
end
end
end
end
-- Search for an icon in specified directories with a specified format
-- @param icon Name of the icon
-- @return full path of the icon, or nil of no icon was found
local function getIcon(name)
for d, dir in pairs(config.icon_dirs) do
for f, format in pairs(config.icon_formats) do
local icon = dir .. name .. "." .. format
if util.file_readable(icon) then
return icon
end
end
end
end
--- Create notification. args is a dictionary of (optional) arguments.
-- @param text Text of the notification. Default: ''
-- @param title Title of the notification. Default: nil
-- @param timeout Time in seconds after which popup expires.
-- Set 0 for no timeout. Default: 5
-- @param hover_timeout Delay in seconds after which hovered popup disappears.
-- Default: nil
-- @param screen Target screen for the notification. Default: 1
-- @param position Corner of the workarea displaying the popups.
-- Values: "top_right" (default), "top_left", "bottom_left", "bottom_right".
-- @param ontop Boolean forcing popups to display on top. Default: true
-- @param height Popup height. Default: nil (auto)
-- @param width Popup width. Default: nil (auto)
-- @param font Notification font. Default: beautiful.font or awesome.font
-- @param icon Path to icon. Default: nil
-- @param icon_size Desired icon size in px. Default: nil
-- @param fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
-- @param bg Background color. Default: beautiful.bg_focus or '#535d6c'
-- @param border_width Border width. Default: 1
-- @param border_color Border color.
-- Default: beautiful.border_focus or '#535d6c'
-- @param run Function to run on left click. Default: nil
-- @param preset Table with any of the above parameters. Note: Any parameters
-- specified directly in args will override ones defined in the preset.
-- @param replaces_id Replace the notification with the given ID
-- @param callback function that will be called with all arguments
-- the notification will only be displayed if the function returns true
-- note: this function is only relevant to notifications sent via dbus
-- @usage naughty.notify({ title = "Achtung!", text = "You're idling", timeout = 0 })
-- @return The notification object
function notify(args)
-- gather variables together
local preset = args.preset or config.default_preset or {}
local timeout = args.timeout or preset.timeout or 5
local icon = args.icon or preset.icon
local icon_size = args.icon_size or preset.icon_size
local text = args.text or preset.text or ""
local title = args.title or preset.title
local screen = args.screen or preset.screen or 1
local ontop = args.ontop or preset.ontop or true
local width = args.width or preset.width
local height = args.height or preset.height
local hover_timeout = args.hover_timeout or preset.hover_timeout
local opacity = args.opacity or preset.opacity
local margin = args.margin or preset.margin or "5"
local border_width = args.border_width or preset.border_width or "1"
local position = args.position or preset.position or "top_right"
-- beautiful
local beautiful = bt.get()
local font = args.font or preset.font or beautiful.font or capi.awesome.font
local fg = args.fg or preset.fg or beautiful.fg_normal or '#ffffff'
local bg = args.bg or preset.bg or beautiful.bg_normal or '#535d6c'
local border_color = args.border_color or preset.border_color or beautiful.bg_focus or '#535d6c'
local notification = {}
-- replace notification if needed
if args.replaces_id then
local obj = getById(args.replaces_id)
if obj then
-- destroy this and ...
destroy(obj)
end
-- ... may use its ID
if args.replaces_id < counter then
notification.id = args.replaces_id
else
counter = counter + 1
notification.id = counter
end
else
-- get a brand new ID
counter = counter + 1
notification.id = counter
end
notification.position = position
if title then title = title .. "\n" else title = "" end
-- hook destroy
local die = function () destroy(notification) end
if timeout > 0 then
local timer_die = capi.timer { timeout = timeout }
timer_die:add_signal("timeout", die)
if not suspended then
timer_die:start()
end
notification.timer = timer_die
end
notification.die = die
local run = function ()
if args.run then
args.run(notification)
else
die()
end
end
local hover_destroy = function ()
if hover_timeout == 0 then
die()
else
if notification.timer then notification.timer:stop() end
notification.timer = capi.timer { timeout = hover_timeout }
notification.timer:add_signal("timeout", die)
notification.timer:start()
end
end
-- create textbox
local textbox = capi.widget({ type = "textbox", align = "flex" })
textbox:buttons(util.table.join(button({ }, 1, run), button({ }, 3, die)))
layout.margins[textbox] = { right = margin, left = margin, bottom = margin, top = margin }
textbox.valign = "middle"
local function setText(pattern, replacements)
textbox.text = string.format('<span font_desc="%s"><b>%s</b>%s</span>', font, title, text:gsub(pattern, replacements))
end
-- First try to set the text while only interpreting <br>.
-- (Setting a textbox' .text to an invalid pattern throws a lua error)
if not pcall(setText, "<br.->", "\n") then
-- That failed, escape everything which might cause an error from pango
if not pcall(setText, "[<>&]", { ['<'] = "&lt;", ['>'] = "&gt;", ['&'] = "&amp;" }) then
textbox.text = "<i>&lt;Invalid markup, cannot display message&gt;</i>"
end
end
-- create iconbox
local iconbox = nil
if icon then
-- try to guess icon if the provided one is non-existent/readable
if type(icon) == "string" and not util.file_readable(icon) then
icon = getIcon(icon)
end
-- if we have an icon, use it
if icon then
iconbox = capi.widget({ type = "imagebox", align = "left" })
layout.margins[iconbox] = { right = margin, left = margin, bottom = margin, top = margin }
iconbox:buttons(util.table.join(button({ }, 1, run), button({ }, 3, die)))
local img
if type(icon) == "string" then
img = capi.image(icon)
else
img = icon
end
if icon_size then
img = img:crop_and_scale(0,0,img.height,img.width,icon_size,icon_size)
end
iconbox.resize = false
iconbox.image = img
end
end
-- create container wibox
notification.box = capi.wibox({ fg = fg,
bg = bg,
border_color = border_color,
border_width = border_width })
if hover_timeout then notification.box:add_signal("mouse::enter", hover_destroy) end
-- calculate the height
if not height then
if iconbox and iconbox:extents().height + 2 * margin > textbox:extents().height + 2 * margin then
height = iconbox:extents().height + 2 * margin
else
height = textbox:extents().height + 2 * margin
end
end
-- calculate the width
if not width then
width = textbox:extents().width + (iconbox and iconbox:extents().width + 2 * margin or 0) + 2 * margin
end
-- crop to workarea size if too big
local workarea = capi.screen[screen].workarea
if width > workarea.width - 2 * (border_width or 0) - 2 * (config.padding or 0) then
width = workarea.width - 2 * (border_width or 0) - 2 * (config.padding or 0)
end
if height > workarea.height - 2 * (border_width or 0) - 2 * (config.padding or 0) then
height = workarea.height - 2 * (border_width or 0) - 2 * (config.padding or 0)
end
-- set size in notification object
notification.height = height + 2 * (border_width or 0)
notification.width = width + 2 * (border_width or 0)
-- position the wibox
local offset = get_offset(screen, notification.position, nil, notification.width, notification.height)
notification.box.ontop = ontop
notification.box:geometry({ width = width,
height = height,
x = offset.x,
y = offset.y })
notification.box.opacity = opacity
notification.box.screen = screen
notification.idx = offset.idx
-- populate widgets
notification.box.widgets = { iconbox, textbox, ["layout"] = layout.horizontal.leftright }
-- insert the notification to the table
table.insert(notifications[screen][notification.position], notification)
if suspended then
notification.box.visible = false
table.insert(notifications.suspended, notification)
end
-- return the notification
return notification
end
-- DBUS/Notification support
-- Notify
if capi.dbus then
capi.dbus.add_signal("org.freedesktop.Notifications", function (data, appname, replaces_id, icon, title, text, actions, hints, expire)
args = { preset = { } }
if data.member == "Notify" then
if text ~= "" then
args.text = text
if title ~= "" then
args.title = title
end
else
if title ~= "" then
args.text = title
else
return
end
end
local score = 0
for i, obj in pairs(config.mapping) do
local filter, preset, s = obj[1], obj[2], 0
if (not filter.urgency or filter.urgency == hints.urgency) and
(not filter.category or filter.category == hints.category) and
(not filter.appname or filter.appname == appname) then
for j, el in pairs(filter) do s = s + 1 end
if s > score then
score = s
args.preset = preset
end
end
end
if not args.preset.callback or (type(args.preset.callback) == "function" and
args.preset.callback(data, appname, replaces_id, icon, title, text, actions, hints, expire)) then
if icon ~= "" then
args.icon = icon
elseif hints.icon_data or hints.image_data then
if hints.icon_data == nil then hints.icon_data = hints.image_data end
-- icon_data is an array:
-- 1 -> width, 2 -> height, 3 -> rowstride, 4 -> has alpha
-- 5 -> bits per sample, 6 -> channels, 7 -> data
local imgdata
-- If has alpha (ARGB32)
if hints.icon_data[6] == 4 then
imgdata = hints.icon_data[7]
-- If has not alpha (RGB24)
elseif hints.icon_data[6] == 3 then
imgdata = ""
for i = 1, #hints.icon_data[7], 3 do
imgdata = imgdata .. hints.icon_data[7]:sub(i , i + 2):reverse()
imgdata = imgdata .. string.format("%c", 255) -- alpha is 255
end
end
if imgdata then
args.icon = capi.image.argb32(hints.icon_data[1], hints.icon_data[2], imgdata)
end
end
if replaces_id and replaces_id ~= "" and replaces_id ~= 0 then
args.replaces_id = replaces_id
end
if expire and expire > -1 then
args.timeout = expire / 1000
end
local id = notify(args).id
return "u", id
end
return "u", "0"
elseif data.member == "CloseNotification" then
local obj = getById(appname)
if obj then
destroy(obj)
end
elseif data.member == "GetServerInfo" or data.member == "GetServerInformation" then
-- name of notification app, name of vender, version
return "s", "naughty", "s", "awesome", "s", capi.awesome.version:match("%d.%d"), "s", "1.0"
elseif data.member == "GetCapabilities" then
-- We actually do display the body of the message, we support <b>, <i>
-- and <u> in the body and we handle static (non-animated) icons.
return "as", { "s", "body", "s", "body-markup", "s", "icon-static" }
end
end)
capi.dbus.add_signal("org.freedesktop.DBus.Introspectable",
function (data, text)
if data.member == "Introspect" then
local xml = [=[<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object
Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.Notifications">
<method name="GetCapabilities">
<arg name="caps" type="as" direction="out"/>
</method>
<method name="CloseNotification">
<arg name="id" type="u" direction="in"/>
</method>
<method name="Notify">
<arg name="app_name" type="s" direction="in"/>
<arg name="id" type="u" direction="in"/>
<arg name="icon" type="s" direction="in"/>
<arg name="summary" type="s" direction="in"/>
<arg name="body" type="s" direction="in"/>
<arg name="actions" type="as" direction="in"/>
<arg name="hints" type="a{sv}" direction="in"/>
<arg name="timeout" type="i" direction="in"/>
<arg name="return_id" type="u" direction="out"/>
</method>
<method name="GetServerInformation">
<arg name="return_name" type="s" direction="out"/>
<arg name="return_vendor" type="s" direction="out"/>
<arg name="return_version" type="s" direction="out"/>
<arg name="return_spec_version" type="s" direction="out"/>
</method>
<method name="GetServerInfo">
<arg name="return_name" type="s" direction="out"/>
<arg name="return_vendor" type="s" direction="out"/>
<arg name="return_version" type="s" direction="out"/>
</method>
</interface>
</node>]=]
return "s", xml
end
end)
-- listen for dbus notification requests
capi.dbus.request_name("session", "org.freedesktop.Notifications")
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,129 @@
-------------------------------------------------------------------
-- Drop-down applications manager for the awesome window manager
-------------------------------------------------------------------
-- Coded by: * Lucas de Vries <lucas@glacicle.com>
-- Hacked by: * Adrian C. (anrxc) <anrxc@sysphere.org>
-- Licensed under the WTFPL version 2
-- * http://sam.zoy.org/wtfpl/COPYING
-------------------------------------------------------------------
-- To use this module add:
-- require("scratch")
-- to the top of your rc.lua, and call it from a keybinding:
-- scratch.drop(prog, vert, horiz, width, height, sticky, screen)
--
-- Parameters:
-- prog - Program to run; "urxvt", "gmrun", "thunderbird"
-- vert - Vertical; "bottom", "center" or "top" (default)
-- horiz - Horizontal; "left", "right" or "center" (default)
-- width - Width in absolute pixels, or width percentage
-- when <= 1 (1 (100% of the screen) by default)
-- height - Height in absolute pixels, or height percentage
-- when <= 1 (0.25 (25% of the screen) by default)
-- sticky - Visible on all tags, false by default
-- screen - Screen (optional), mouse.screen by default
-------------------------------------------------------------------
-- Grab environment
local pairs = pairs
local awful = require("awful")
local setmetatable = setmetatable
local capi = {
mouse = mouse,
client = client,
screen = screen
}
-- Scratchdrop: drop-down applications manager for the awesome window manager
module("scratch.drop")
local dropdown = {}
-- Create a new window for the drop-down application when it doesn't
-- exist, or toggle between hidden and visible states when it does
function toggle(prog, vert, horiz, width, height, sticky, screen)
vert = vert or "top"
horiz = horiz or "center"
width = width or 1
height = height or 0.25
sticky = sticky or false
screen = screen or capi.mouse.screen
if not dropdown[prog] then
dropdown[prog] = {}
-- Add unmanage signal for scratchdrop programs
capi.client.add_signal("unmanage", function (c)
for scr, cl in pairs(dropdown[prog]) do
if cl == c then
dropdown[prog][scr] = nil
end
end
end)
end
if not dropdown[prog][screen] then
spawnw = function (c)
dropdown[prog][screen] = c
-- Scratchdrop clients are floaters
awful.client.floating.set(c, true)
-- Client geometry and placement
local screengeom = capi.screen[screen].workarea
if width <= 1 then width = screengeom.width * width end
if height <= 1 then height = screengeom.height * height end
if horiz == "left" then x = screengeom.x
elseif horiz == "right" then x = screengeom.width - width
else x = screengeom.x+(screengeom.width-width)/2 end
if vert == "bottom" then y = screengeom.height + screengeom.y - height
elseif vert == "center" then y = screengeom.y+(screengeom.height-height)/2
else y = screengeom.y - screengeom.y end
-- Client properties
c:geometry({ x = x, y = y, width = width, height = height })
c.ontop = true
c.above = true
c.skip_taskbar = true
if sticky then c.sticky = true end
if c.titlebar then awful.titlebar.remove(c) end
c:raise()
capi.client.focus = c
capi.client.remove_signal("manage", spawnw)
end
-- Add manage signal and spawn the program
capi.client.add_signal("manage", spawnw)
awful.util.spawn(prog, false)
else
-- Get a running client
c = dropdown[prog][screen]
-- Switch the client to the current workspace
if c:isvisible() == false then c.hidden = true
awful.client.movetotag(awful.tag.selected(screen), c)
end
-- Focus and raise if hidden
if c.hidden then
-- Make sure it is centered
if vert == "center" then awful.placement.center_vertical(c) end
if horiz == "center" then awful.placement.center_horizontal(c) end
c.hidden = false
c:raise()
capi.client.focus = c
else -- Hide and detach tags if not
c.hidden = true
local ctags = c:tags()
for i, t in pairs(ctags) do
ctags[i] = nil
end
c:tags(ctags)
end
end
end
setmetatable(_M, { __call = function(_, ...) return toggle(...) end })

View File

@ -0,0 +1,12 @@
---------------------------------------------------------------
-- Drop-down applications and scratchpad manager for awesome wm
---------------------------------------------------------------
-- Coded by: * Adrian C. (anrxc) <anrxc@sysphere.org>
-- Licensed under the WTFPL version 2
-- * http://sam.zoy.org/wtfpl/COPYING
---------------------------------------------------------------
require("scratch.pad")
require("scratch.drop")
module("scratch")

View File

@ -0,0 +1,130 @@
---------------------------------------------------------------
-- Basic scratchpad manager for the awesome window manager
---------------------------------------------------------------
-- Coded by: * Adrian C. (anrxc) <anrxc@sysphere.org>
-- Licensed under the WTFPL version 2
-- * http://sam.zoy.org/wtfpl/COPYING
---------------------------------------------------------------
-- To use this module add:
-- require("scratch")
-- to the top of your rc.lua, and call:
-- scratch.pad.set(c, width, height, sticky, screen)
-- from a clientkeys binding, and:
-- scratch.pad.toggle(screen)
-- from a globalkeys binding.
--
-- Parameters:
-- c - Client to scratch or un-scratch
-- width - Width in absolute pixels, or width percentage
-- when <= 1 (0.50 (50% of the screen) by default)
-- height - Height in absolute pixels, or height percentage
-- when <= 1 (0.50 (50% of the screen) by default)
-- sticky - Visible on all tags, false by default
-- screen - Screen (optional), mouse.screen by default
---------------------------------------------------------------
-- Grab environment
local pairs = pairs
local awful = require("awful")
local capi = {
mouse = mouse,
client = client,
screen = screen
}
-- Scratchpad: basic scratchpad manager for the awesome window manager
module("scratch.pad")
local scratchpad = {}
-- Toggle a set of properties on a client.
local function toggleprop(c, prop)
c.ontop = prop.ontop or false
c.above = prop.above or false
c.hidden = prop.hidden or false
c.sticky = prop.stick or false
c.skip_taskbar = prop.task or false
end
-- Scratch the focused client, or un-scratch and tile it. If another
-- client is already scratched, replace it with the focused client.
function set(c, width, height, sticky, screen)
width = width or 0.50
height = height or 0.50
sticky = sticky or false
screen = screen or capi.mouse.screen
local function setscratch(c)
-- Scratchpad is floating and has no titlebar
awful.client.floating.set(c, true); awful.titlebar.remove(c)
-- Scratchpad client properties
toggleprop(c, {ontop=true, above=true, task=true, stick=sticky})
-- Scratchpad geometry and placement
local screengeom = capi.screen[screen].workarea
if width <= 1 then width = screengeom.width * width end
if height <= 1 then height = screengeom.height * height end
c:geometry({ -- Scratchpad is always centered on screen
x = screengeom.x + (screengeom.width - width) / 2,
y = screengeom.y + (screengeom.height - height) / 2,
width = width, height = height
})
-- Scratchpad should not loose focus
c:raise(); capi.client.focus = c
end
-- Prepare a table for storing clients,
if not scratchpad.pad then scratchpad.pad = {}
-- add unmanage signal for scratchpad clients
capi.client.add_signal("unmanage", function (c)
for scr, cl in pairs(scratchpad.pad) do
if cl == c then scratchpad.pad[scr] = nil end
end
end)
end
-- If the scratcphad is emtpy, store the client,
if not scratchpad.pad[screen] then
scratchpad.pad[screen] = c
-- then apply geometry and properties
setscratch(c)
else -- If a client is already scratched,
local oc = scratchpad.pad[screen]
-- unscratch, and compare it with the focused client
awful.client.floating.toggle(oc); toggleprop(oc, {})
-- If it matches clear the table, if not replace it
if oc == c then scratchpad.pad[screen] = nil
else scratchpad.pad[screen] = c; setscratch(c) end
end
end
-- Move the scratchpad to the current workspace, focus and raise it
-- when it's hidden, or hide it when it's visible.
function toggle(screen)
screen = screen or capi.mouse.screen
-- Check if we have a client on storage,
if scratchpad.pad and
scratchpad.pad[screen] ~= nil
then -- and get it out, to play
local c = scratchpad.pad[screen]
-- If it's visible on another tag hide it,
if c:isvisible() == false then c.hidden = true
-- and move it to the current worskpace
awful.client.movetotag(awful.tag.selected(screen), c)
end
-- Focus and raise if it's hidden,
if c.hidden then
awful.placement.centered(c)
c.hidden = false
c:raise(); capi.client.focus = c
else -- hide it if it's not
c.hidden = true
end
end
end

View File

@ -0,0 +1,51 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = { match = string.match }
-- }}}
-- Batacpi: provides state, charge, and remaining time for all batteries using acpitool
module("vicious.contrib.batacpi")
-- {{{ Battery widget type
local function worker(format)
local battery_info = {}
local battery_state = {
["full"] = "",
["unknown"] = "",
["charged"] = "",
["charging"] = "+",
["discharging"] = "-"
}
-- Get data from acpitool
local f = io.popen("acpitool -b")
for line in f:lines() do
-- Check if the battery is present
if string.match(line, "^[%s]+Battery.*") then
-- Store state and charge information
table.insert(battery_info, (battery_state[string.match(line, "([%a]*),") or "unknown"]))
table.insert(battery_info, (tonumber(string.match(line, "([%d]?[%d]?[%d])%.")) or 0))
-- Store remaining time information
table.insert(battery_info, (string.match(line, "%%,%s(.*)") or "N/A"))
else
return {battery_state["unknown"], 0, "N/A"}
end
end
f:close()
return battery_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,78 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local math = {
min = math.min,
floor = math.floor
}
local string = {
find = string.find,
match = string.match,
format = string.format
}
-- }}}
-- Batpmu: provides state, charge and remaining time for a requested battery using PMU
module("vicious.contrib.batpmu")
-- {{{ Battery widget type
local function worker(format, batid)
local battery_state = {
["full"] = "",
["unknown"] = "",
["00000013"] = "+",
["00000011"] = "-"
}
-- Get /proc/pmu/battery* state
local f = io.open("/proc/pmu/" .. batid)
-- Handler for incompetent users
if not f then return {battery_state["unknown"], 0, "N/A"} end
local statefile = f:read("*all")
f:close()
-- Get /proc/pmu/info data
local f = io.open("/proc/pmu/info")
local infofile = f:read("*all")
f:close()
-- Check if the battery is present
if infofile == nil or string.find(infofile, "Battery count[%s]+:[%s]0") then
return {battery_state["unknown"], 0, "N/A"}
end
-- Get capacity and charge information
local capacity = string.match(statefile, "max_charge[%s]+:[%s]([%d]+).*")
local remaining = string.match(statefile, "charge[%s]+:[%s]([%d]+).*")
-- Calculate percentage
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Get timer information
local timer = string.match(statefile, "time rem%.[%s]+:[%s]([%d]+).*")
if timer == "0" then return {battery_state["full"], percent, "N/A"} end
-- Get state information
local state = string.match(statefile, "flags[%s]+:[%s]([%d]+).*")
local state = battery_state[state] or battery_state["unknown"]
-- Calculate remaining (charging or discharging) time
local hoursleft = math.floor(tonumber(timer) / 3600)
local minutesleft = math.floor((tonumber(timer) / 60) % 60)
local time = string.format("%02d:%02d", hoursleft, minutesleft)
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local math = {
min = math.min,
floor = math.floor
}
local string = {
find = string.find,
match = string.match,
format = string.format
}
-- }}}
-- Batproc: provides state, charge, and remaining time for a requested battery using procfs
module("vicious.contrib.batproc")
-- {{{ Battery widget type
local function worker(format, batid)
local battery_state = {
["full"] = "",
["unknown"] = "",
["charged"] = "",
["charging"] = "+",
["discharging"] = "-"
}
-- Get /proc/acpi/battery info
local f = io.open("/proc/acpi/battery/"..batid.."/info")
-- Handler for incompetent users
if not f then return {battery_state["unknown"], 0, "N/A"} end
local infofile = f:read("*all")
f:close()
-- Check if the battery is present
if infofile == nil or string.find(infofile, "present:[%s]+no") then
return {battery_state["unknown"], 0, "N/A"}
end
-- Get capacity information
local capacity = string.match(infofile, "last full capacity:[%s]+([%d]+).*")
-- Get /proc/acpi/battery state
local f = io.open("/proc/acpi/battery/"..batid.."/state")
local statefile = f:read("*all")
f:close()
-- Get state information
local state = string.match(statefile, "charging state:[%s]+([%a]+).*")
local state = battery_state[state] or battery_state["unknown"]
-- Get charge information
local rate = string.match(statefile, "present rate:[%s]+([%d]+).*")
local remaining = string.match(statefile, "remaining capacity:[%s]+([%d]+).*")
-- Calculate percentage (but work around broken BAT/ACPI implementations)
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Calculate remaining (charging or discharging) time
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "-" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, "N/A"}
end
local hoursleft = math.floor(timeleft)
local minutesleft = math.floor((timeleft - hoursleft) * 60 )
local time = string.format("%02d:%02d", hoursleft, minutesleft)
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local ipairs = ipairs
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Disk I/O: provides I/O statistics for requested storage devices
module("vicious.contrib.dio")
-- Initialize function tables
local disk_usage = {}
local disk_total = {}
-- Variable definitions
local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 }
-- {{{ Disk I/O widget type
local function worker(format, disk)
if not disk then return end
local disk_lines = { [disk] = {} }
local disk_stats = helpers.pathtotable("/sys/block/" .. disk)
if disk_stats.stat then
local match = string.gmatch(disk_stats.stat, "[%s]+([%d]+)")
for i = 1, 11 do -- Store disk stats
table.insert(disk_lines[disk], match())
end
end
-- Ensure tables are initialized correctly
local diff_total = { [disk] = {} }
if not disk_total[disk] then
disk_usage[disk] = {}
disk_total[disk] = {}
while #disk_total[disk] < #disk_lines[disk] do
table.insert(disk_total[disk], 0)
end
end
for i, v in ipairs(disk_lines[disk]) do
-- Diskstats are absolute, substract our last reading
diff_total[disk][i] = v - disk_total[disk][i]
-- Store totals
disk_total[disk][i] = v
end
-- Calculate and store I/O
helpers.uformat(disk_usage[disk], "read", diff_total[disk][3], unit)
helpers.uformat(disk_usage[disk], "write", diff_total[disk][7], unit)
helpers.uformat(disk_usage[disk], "total", diff_total[disk][7] + diff_total[disk][3], unit)
-- Store I/O scheduler
if disk_stats.queue and disk_stats.queue.scheduler then
disk_usage[disk]["{sched}"] = string.gmatch(disk_stats.queue.scheduler, "%[([%a]+)%]")
end
return disk_usage[disk]
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,17 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Setup environment
local setmetatable = setmetatable
local wrequire = require("vicious.helpers").wrequire
-- Vicious: widgets for the awesome window manager
module("vicious.contrib")
-- }}}
-- Load modules at runtime as needed
setmetatable(_M, { __index = wrequire })

View File

@ -0,0 +1,47 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { find = string.find }
local helpers = require("vicious.helpers")
-- }}}
-- Mpc: provides the currently playing song in MPD
module("vicious.contrib.mpc")
-- {{{ MPC widget type
local function worker(format, warg)
-- Get data from mpd
local f = io.popen("mpc")
local np = f:read("*line")
f:close()
-- Not installed,
if np == nil or -- off or stoppped.
(string.find(np, "MPD_HOST") or string.find(np, "volume:"))
then
return {"Stopped"}
end
-- Check if we should scroll, or maybe truncate
if warg then
if type(warg) == "table" then
np = helpers.scroll(np, warg[1], warg[2])
else
np = helpers.truncate(np, warg)
end
end
return {helpers.escape(np)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,138 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local tonumber = tonumber
local os = { time = os.time }
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Net: provides usage statistics for all network interfaces
module("vicious.contrib.net")
-- Initialise function tables
local nets = {}
-- Variable definitions
local unit = { ["b"] = 1, ["kb"] = 1024,
["mb"] = 1024^2, ["gb"] = 1024^3
}
-- {{{ Net widget type
local function worker(format, tignorelist)
local args = {}
local tignore = {}
local total_rx = 0
local total_tx = 0
local any_up = 0
if not tignorelist then
tignorelist = {"lo", "wmaster0"}
end
for k, i in pairs(tignorelist) do
tignore[i] = true
end
-- Get NET stats
for line in io.lines("/proc/net/dev") do
-- Match wmaster0 as well as rt0 (multiple leading spaces)
local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):")
if name ~= nil then
-- Received bytes, first value after the name
local recv = tonumber(string.match(line, ":[%s]*([%d]+)"))
-- Transmited bytes, 7 fields from end of the line
local send = tonumber(string.match(line,
"([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$"))
if not tignore[name] then
total_rx = total_rx + recv
total_tx = total_tx + send
end
helpers.uformat(args, name .. " rx", recv, unit)
helpers.uformat(args, name .. " tx", send, unit)
if nets[name] == nil then
-- Default values on the first run
nets[name] = {}
helpers.uformat(args, name .. " down", 0, unit)
helpers.uformat(args, name .. " up", 0, unit)
args["{"..name.." carrier}"] = 0
nets[name].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets[name].time > 0 and
os.time() - nets[name].time or 1
nets[name].time = os.time()
local down = (recv - nets[name][1]) / interval
local up = (send - nets[name][2]) / interval
helpers.uformat(args, name .. " down", down, unit)
helpers.uformat(args, name .. " up", up, unit)
-- Carrier detection
sysnet = helpers.pathtotable("/sys/class/net/" .. name)
if sysnet.carrier then
ccarrier = tonumber(sysnet.carrier)
args["{"..name.." carrier}"] = ccarrier
if ccarrier ~= 0 and not tignore[name] then
any_up = 1
end
else
args["{"..name.." carrier}"] = 0
end
end
-- Store totals
nets[name][1] = recv
nets[name][2] = send
end
end
helpers.uformat(args, "total rx", total_rx, unit)
helpers.uformat(args, "total tx", total_tx, unit)
if nets["total"] == nil then
-- Default values on the first run
nets["total"] = {}
helpers.uformat(args, "total down", 0, unit)
helpers.uformat(args, "total up", 0, unit)
args["{total carrier}"] = 0
nets["total"].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets["total"].time > 0 and
os.time() - nets["total"].time or 1
nets["total"].time = os.time()
local down = (total_rx - nets["total"][1]) / interval
local up = (total_tx - nets["total"][2]) / interval
helpers.uformat(args, "total down", down, unit)
helpers.uformat(args, "total up", up, unit)
args["{total carrier}"] = any_up
end
-- Store totals
nets["total"][1] = total_rx
nets["total"][2] = total_tx
return args
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,34 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Radu A. <admiral0@tuxfamily.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
-- }}}
-- Netcfg: provides active netcfg network profiles
module("vicious.contrib.netcfg")
-- {{{ Netcfg widget type
local function worker(format)
-- Initialize counters
local profiles = {}
local f = io.popen("ls -1 /var/run/network/profiles")
for line in f:lines() do
if line ~= nil then
table.insert(profiles, line)
end
end
f:close()
return profiles
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,53 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
-- }}}
-- Ossvol: provides volume levels of requested OSS mixers
module("vicious.contrib.ossvol")
-- {{{ Volume widget type
local function worker(format, warg)
if not warg then return end
local mixer_state = {
["on"] = "", -- "",
["off"] = "" -- "M"
}
-- Get mixer control contents
local f = io.popen("ossmix -c")
local mixer = f:read("*all")
f:close()
-- Capture mixer control state
local volu = tonumber(string.match(mixer, warg .. "[%s]([%d%.]+)"))/0.25
local mute = string.match(mixer, "vol%.mute[%s]([%a]+)")
-- Handle mixers without data
if volu == nil then
return {0, mixer_state["off"]}
end
-- Handle mixers without mute
if mute == "OFF" and volu == "0"
-- Handle mixers that are muted
or mute == "ON" then
mute = mixer_state["off"]
else
mute = mixer_state["on"]
end
return {volu, mute}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,54 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Boris Bolgradov <>
--
-- This widget type depends on luasocket.
--
-- Widget arguments are host, port, username and
-- password, i.e.:
-- {"mail.myhost.com", 110, "John", "132435"}
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local sock_avail, socket = pcall(function()
return require("socket")
end)
-- }}}
-- POP: provides the count of new messages in a POP3 mailbox
module("vicious.contrib.pop")
-- {{{ POP3 count widget type
local function worker(format, warg)
if not sock_avail or (not warg or #warg ~= 4) then
return {"N/A"}
end
local host, port = warg[1], tonumber(warg[2])
local user, pass = warg[3], warg[4]
local client = socket.tcp()
client:settimeout(3)
client:connect(host, port)
client:receive("*l")
client:send("USER " .. user .. "\r\n")
client:receive("*l")
client:send("PASS " .. pass .. "\r\n")
client:receive("*l")
client:send("STAT" .. "\r\n")
local response = client:receive("*l")
client:close()
if response:find("%+OK") then
response = response:match("%+OK (%d+)")
end
return {response}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,111 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, MrMagne <mr.magne@yahoo.fr>
-- * (c) 2010, Mic92 <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local os = { execute = os.execute }
local table = { insert = table.insert }
local string = {
find = string.find,
match = string.match,
format = string.format,
gmatch = string.gmatch
}
-- }}}
-- Pulse: provides volume levels of requested pulseaudio sinks and methods to change them
module("vicious.contrib.pulse")
-- {{{ Helper function
local function pacmd(args)
local f = io.popen("pacmd "..args)
local line = f:read("*all")
f:close()
return line
end
local function escape(text)
local special_chars = { ["."] = "%.", ["-"] = "%-" }
return text:gsub("[%.%-]", special_chars)
end
local cached_sinks = {}
local function get_sink_name(sink)
if type(sink) == "string" then return sink end
-- avoid nil keys
local key = sink or 1
-- Cache requests
if not cached_sinks[key] then
local line = pacmd("list-sinks")
for s in string.gmatch(line, "name: <(.-)>") do
table.insert(cached_sinks, s)
end
end
return cached_sinks[key]
end
-- }}}
-- {{{ Pulseaudio widget type
local function worker(format, sink)
sink = get_sink_name(sink)
if sink == nil then return {0, "unknown"} end
-- Get sink data
local data = pacmd("dump")
-- If mute return 0 (not "Mute") so we don't break progressbars
if string.find(data,"set%-sink%-mute "..escape(sink).." yes") then
return {0, "off"}
end
local vol = tonumber(string.match(data, "set%-sink%-volume "..escape(sink).." (0x[%x]+)"))
if vol == nil then vol = 0 end
return { vol/0x10000*100, "on"}
end
-- }}}
-- {{{ Volume control helper
function add(percent, sink)
sink = get_sink_name(sink)
if sink == nil then return end
local data = pacmd("dump")
local pattern = "set%-sink%-volume "..escape(sink).." (0x[%x]+)"
local initial_vol = tonumber(string.match(data, pattern))
local vol = initial_vol + percent/100*0x10000
if vol > 0x10000 then vol = 0x10000 end
if vol < 0 then vol = 0 end
local cmd = string.format("pacmd set-sink-volume %s 0x%x >/dev/null", sink, vol)
return os.execute(cmd)
end
function toggle(sink)
sink = get_sink_name(sink)
if sink == nil then return end
local data = pacmd("dump")
local pattern = "set%-sink%-mute "..escape(sink).." (%a%a%a?)"
local mute = string.match(data, pattern)
-- 0 to enable a sink or 1 to mute it.
local state = { yes = 0, no = 1}
local cmd = string.format("pacmd set-sink-mute %s %d", sink, state[mute])
return os.execute(cmd)
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,67 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2009, olcc
--
-- This is now a standalone RSS reader for awesome:
-- * http://github.com/olcc/aware
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local io = { popen = io.popen }
local setmetatable = setmetatable
-- }}}
-- RSS: provides latest world news
module("vicious.contrib.rss")
-- {{{ RSS widget type
local function worker(format, input)
-- input: * feed - feed url
-- * object - entity to look for (typically: 'item')
-- * fields - fields to read (example: 'link', 'title', 'description')
-- output: * count - number of entities found
-- * one table for each field, containing wanted values
local feed = input.feed
local object = input.object
local fields = input.fields
-- Initialise tables
local out = {}
for _, v in pairs(fields) do
out[v] = {}
end
-- Initialise variables
local ob = nil
local i,j,k = 1, 1, 0
local curl = "curl -A 'Mozilla/4.0' -fsm 5 --connect-timeout 3 "
-- Get the feed
local f = io.popen(curl .. '"' .. feed .. '"')
local feed = f:read("*all")
f:close()
while true do
i, j, ob = feed.find(feed, "<" .. object .. ">(.-)</" .. object .. ">", i)
if not ob then break end
for _, v in pairs(fields) do
out[v][k] = ob:match("<" .. v .. ">(.*)</" .. v .. ">")
end
k = k+1
i = j+1
end
-- Update the entity count
out.count = k
return out
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,68 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Greg D. <jabbas@jabbas.pl>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = {
gsub = string.gsub,
match = string.match
}
-- }}}
-- Sensors: provides access to lm_sensors data
module("vicious.contrib.sensors")
-- {{{ Split helper function
local function datasplit(str)
-- Splitting strings into associative array
-- with some magic to get the values right.
str = string.gsub(str, "\n", ":")
local tbl = {}
string.gsub(str, "([^:]*)", function (v)
if string.match(v, ".") then
table.insert(tbl, v)
end
end)
local assoc = {}
for c = 1, #tbl, 2 do
local k = string.gsub(tbl[c], ".*_", "")
local v = tonumber(string.match(tbl[c+1], "[%d]+"))
assoc[k] = v
end
return assoc
end
-- }}}
-- {{{ Sensors widget type
local function worker(format, warg)
-- Get data from all sensors
local f = io.popen("LANG=C sensors -uA")
local lm_sensors = f:read("*all")
f:close()
local sensor_data = string.gsub(
string.match(lm_sensors, warg..":\n(%s%s.-)\n[^ ]"), " ", "")
-- One of: crit, max
local divisor = "crit"
local s_data = datasplit(sensor_data)
if s_data[divisor] and s_data[divisor] > 0 then
s_data.percent = s_data.input / s_data[divisor] * 100
end
return {s_data.input, tonumber(s_data.percent)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,149 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Rémy C. <shikamaru@mandriva.org>
-- * (c) 2009, Benedikt Sauer <filmor@gmail.com>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local rawget = rawget
local require = require
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local getmetatable = getmetatable
local string = {
upper = string.upper,
format = string.format
}
-- }}}
-- Helpers: provides helper functions for vicious widgets
module("vicious.helpers")
-- {{{ Variable definitions
local scroller = {}
-- }}}
-- {{{ Helper functions
-- {{{ Loader of vicious modules
function wrequire(table, key)
local module = rawget(table, key)
return module or require(table._NAME .. "." .. key)
end
-- }}}
-- {{{ Expose path as a Lua table
function pathtotable(dir)
return setmetatable({ _path = dir },
{ __index = function(table, index)
local path = table._path .. '/' .. index
local f = io.open(path)
if f then
local s = f:read("*all")
f:close()
if s then
return s
else
local o = { _path = path }
setmetatable(o, getmetatable(table))
return o
end
end
end
})
end
-- }}}
-- {{{ Format a string with args
function format(format, args)
for var, val in pairs(args) do
format = format:gsub("$" .. (tonumber(var) and var or
var:gsub("[-+?*]", function(i) return "%"..i end)),
val)
end
return format
end
-- }}}
-- {{{ Format units to one decimal point
function uformat(array, key, value, unit)
for u, v in pairs(unit) do
array["{"..key.."_"..u.."}"] = string.format("%.1f", value/v)
end
return array
end
-- }}}
-- {{{ Escape a string
function escape(text)
local xml_entities = {
["\""] = "&quot;",
["&"] = "&amp;",
["'"] = "&apos;",
["<"] = "&lt;",
[">"] = "&gt;"
}
return text and text:gsub("[\"&'<>]", xml_entities)
end
-- }}}
-- {{{ Capitalize a string
function capitalize(text)
return text and text:gsub("([%w])([%w]*)", function(c, s)
return string.upper(c) .. s
end)
end
-- }}}
-- {{{ Truncate a string
function truncate(text, maxlen)
local txtlen = text:len()
if txtlen > maxlen then
text = text:sub(1, maxlen - 3) .. "..."
end
return text
end
-- }}}
-- {{{ Scroll through a string
function scroll(text, maxlen, widget)
if not scroller[widget] then
scroller[widget] = { i = 1, d = true }
end
local txtlen = text:len()
local state = scroller[widget]
if txtlen > maxlen then
if state.d then
text = text:sub(state.i, state.i + maxlen) .. "..."
state.i = state.i + 3
if maxlen + state.i >= txtlen then
state.d = false
end
else
text = "..." .. text:sub(state.i, state.i + maxlen)
state.i = state.i - 3
if state.i <= 1 then
state.d = true
end
end
end
return text
end
-- }}}
-- }}}

View File

@ -0,0 +1,249 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Setup environment
local type = type
local pairs = pairs
local tonumber = tonumber
local capi = { timer = timer }
local os = { time = os.time }
local table = {
insert = table.insert,
remove = table.remove
}
require("vicious.helpers")
require("vicious.widgets")
--require("vicious.contrib")
-- Vicious: widgets for the awesome window manager
module("vicious")
-- Initialize tables
local timers = {}
local registered = {}
local widget_cache = {}
-- }}}
-- {{{ Local functions
-- {{{ Update a widget
local function update(widget, reg, disablecache)
-- Check if there are any equal widgets
if reg == nil then
for w, i in pairs(registered) do
if w == widget then
for _, r in pairs(i) do
update(w, r, disablecache)
end
end
end
return
end
local t = os.time()
local data = {}
-- Check for chached output newer than the last update
if widget_cache[reg.wtype] ~= nil then
local c = widget_cache[reg.wtype]
if (c.time == nil or c.time <= t-reg.timer) or disablecache then
c.time, c.data = t, reg.wtype(reg.format, reg.warg)
end
data = c.data
else
data = reg.wtype and reg.wtype(reg.format, reg.warg)
end
if type(data) == "table" then
if type(reg.format) == "string" then
data = helpers.format(reg.format, data)
elseif type(reg.format) == "function" then
data = reg.format(widget, data)
end
end
if widget.add_value ~= nil then
widget:add_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_value ~= nil then
widget:set_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_markup ~= nil then
widget:set_markup(data)
else
widget.text = data
end
return data
end
-- }}}
-- {{{ Register from reg object
local function regregister(reg)
if not reg.running then
if registered[reg.widget] == nil then
registered[reg.widget] = {}
table.insert(registered[reg.widget], reg)
else
local already = false
for w, i in pairs(registered) do
if w == reg.widget then
for _, v in pairs(i) do
if v == reg then
already = true
break
end
end
if already then
break
end
end
end
if not already then
table.insert(registered[reg.widget], reg)
end
end
-- Start the timer
if reg.timer > 0 then
timers[reg.update] = {
timer = capi.timer({ timeout = reg.timer })
}
local tm = timers[reg.update].timer
if tm.connect_signal then
tm:connect_signal("timeout", reg.update)
else
tm:add_signal("timeout", reg.update)
end
tm:start()
-- Initial update
tm:emit_signal("timeout")
end
reg.running = true
end
end
-- }}}
-- }}}
-- {{{ Global functions
-- {{{ Register a widget
function register(widget, wtype, format, timer, warg)
local reg = {}
local widget = widget
-- Set properties
reg.wtype = wtype
reg.format = format
reg.timer = timer
reg.warg = warg
reg.widget = widget
-- Update function
reg.update = function ()
update(widget, reg)
end
-- Default to 2s timer
if reg.timer == nil then
reg.timer = 2
end
-- Register a reg object
regregister(reg)
-- Return a reg object for reuse
return reg
end
-- }}}
-- {{{ Unregister a widget
function unregister(widget, keep, reg)
if reg == nil then
for w, i in pairs(registered) do
if w == widget then
for _, v in pairs(i) do
reg = unregister(w, keep, v)
end
end
end
return reg
end
if not keep then
for w, i in pairs(registered) do
if w == widget then
for k, v in pairs(i) do
if v == reg then
table.remove(registered[w], k)
end
end
end
end
end
-- Stop the timer
if timers[reg.update].timer.started then
timers[reg.update].timer:stop()
end
reg.running = false
return reg
end
-- }}}
-- {{{ Enable caching of a widget type
function cache(wtype)
if wtype ~= nil then
if widget_cache[wtype] == nil then
widget_cache[wtype] = {}
end
end
end
-- }}}
-- {{{ Force update of widgets
function force(wtable)
if type(wtable) == "table" then
for _, w in pairs(wtable) do
update(w, nil, true)
end
end
end
-- }}}
-- {{{ Suspend all widgets
function suspend()
for w, i in pairs(registered) do
for _, v in pairs(i) do
unregister(w, true, v)
end
end
end
-- }}}
-- {{{ Activate a widget
function activate(widget)
for w, i in pairs(registered) do
if widget == nil or w == widget then
for _, v in pairs(i) do
regregister(v)
end
end
end
end
-- }}}
-- }}}

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local string = { format = string.format }
local helpers = require("vicious.helpers")
local math = {
min = math.min,
floor = math.floor
}
-- }}}
-- Bat: provides state, charge, and remaining time for a requested battery
module("vicious.widgets.bat")
-- {{{ Battery widget type
local function worker(format, warg)
if not warg then return end
local battery = helpers.pathtotable("/sys/class/power_supply/"..warg)
local battery_state = {
["Full\n"] = "",
["Unknown\n"] = "",
["Charged\n"] = "",
["Charging\n"] = "+",
["Discharging\n"] = "-"
}
-- Check if the battery is present
if battery.present ~= "1\n" then
return {battery_state["Unknown\n"], 0, "N/A"}
end
-- Get state information
local state = battery_state[battery.status] or battery_state["Unknown\n"]
-- Get capacity information
if battery.charge_now then
remaining, capacity = battery.charge_now, battery.charge_full
elseif battery.energy_now then
remaining, capacity = battery.energy_now, battery.energy_full
else
return {battery_state["Unknown\n"], 0, "N/A"}
end
-- Calculate percentage (but work around broken BAT/ACPI implementations)
local percent = math.min(math.floor(remaining / capacity * 100), 100)
-- Get charge information
if battery.current_now then
rate = battery.current_now
elseif battery.power_now then
rate = battery.power_now
else
return {state, percent, "N/A"}
end
-- Calculate remaining (charging or discharging) time
local time = "N/A"
if rate ~= nil then
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "-" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, time}
end
local hoursleft = math.floor(timeleft)
local minutesleft = math.floor((timeleft - hoursleft) * 60 )
time = string.format("%02d:%02d", hoursleft, minutesleft)
end
return {state, percent, time}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,75 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
-- * (c) 2011, Jörg Thalheim <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local ipairs = ipairs
local io = { lines = io.lines }
local setmetatable = setmetatable
local math = { floor = math.floor }
local table = { insert = table.insert }
local string = {
sub = string.sub,
gmatch = string.gmatch
}
-- }}}
-- Cpu: provides CPU usage for all available CPUs/cores
module("vicious.widgets.cpu")
-- Initialize function tables
local cpu_usage = {}
local cpu_total = {}
local cpu_active = {}
-- {{{ CPU widget type
local function worker(format)
local cpu_lines = {}
-- Get CPU stats
for line in io.lines("/proc/stat") do
if string.sub(line, 1, 3) ~= "cpu" then break end
cpu_lines[#cpu_lines+1] = {}
for i in string.gmatch(line, "[%s]+([^%s]+)") do
table.insert(cpu_lines[#cpu_lines], i)
end
end
-- Ensure tables are initialized correctly
for i = #cpu_total + 1, #cpu_lines do
cpu_total[i] = 0
cpu_usage[i] = 0
cpu_active[i] = 0
end
for i, v in ipairs(cpu_lines) do
-- Calculate totals
local total_new = 0
for j = 1, #v do
total_new = total_new + v[j]
end
local active_new = total_new - (v[4] + v[5])
-- Calculate percentage
local diff_total = total_new - cpu_total[i]
local diff_active = active_new - cpu_active[i]
cpu_usage[i] = math.floor((diff_active / diff_total) * 100)
-- Store totals
cpu_total[i] = total_new
cpu_active[i] = active_new
end
return cpu_usage
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,56 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Cpufreq: provides freq, voltage and governor info for a requested CPU
module("vicious.widgets.cpufreq")
-- {{{ CPU frequency widget type
local function worker(format, warg)
if not warg then return end
local cpufreq = helpers.pathtotable("/sys/devices/system/cpu/"..warg.."/cpufreq")
local governor_state = {
["ondemand\n"] = "",
["powersave\n"] = "",
["userspace\n"] = "¤",
["performance\n"] = "",
["conservative\n"] = ""
}
-- Default voltage values
local voltage = { v = "N/A", mv = "N/A" }
-- Get the current frequency
local freq = tonumber(cpufreq.scaling_cur_freq)
-- Calculate MHz and GHz
local freqmhz = freq / 1000
local freqghz = freqmhz / 1000
-- Get the current voltage
if cpufreq.scaling_voltages then
voltage.mv = tonumber(string.match(cpufreq.scaling_voltages, freq.."[%s]([%d]+)"))
-- Calculate voltage from mV
voltage.v = voltage.mv / 1000
end
-- Get the current governor
local governor = cpufreq.scaling_governor
-- Represent the governor as a symbol
governor = governor_state[governor] or governor
return {freqmhz, freqghz, voltage.mv, voltage.v, governor}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,43 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
-- }}}
-- Cpuinf: provides speed and cache information for all available CPUs/cores
module("vicious.widgets.cpuinf")
-- {{{ CPU Information widget type
local function worker(format)
local id = nil
local cpu_info = {} -- Get CPU info
for line in io.lines("/proc/cpuinfo") do
for k, v in string.gmatch(line, "([%a%s]+)[%s]+:[%s]([%d]+).-$") do
if k == "processor" then
id = v
elseif k == "cpu MHz\t" or k == "cpu MHz" then
local speed = tonumber(v)
cpu_info["{cpu"..id.." mhz}"] = speed
cpu_info["{cpu"..id.." ghz}"] = speed / 1000
elseif k == "cache size" then
local cache = tonumber(v)
cpu_info["{cpu"..id.." kb}"] = cache
cpu_info["{cpu"..id.." mb}"] = cache / 1024
end
end
end
return cpu_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,26 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local os = {
date = os.date,
time = os.time
}
-- }}}
-- Date: provides access to os.date with optional time formatting
module("vicious.widgets.date")
-- {{{ Date widget type
local function worker(format, warg)
return os.date(format or nil, warg and os.time()+warg or nil)
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Jörg T. <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
local os = {
time = os.time,
difftime = os.difftime
}
-- }}}
-- Disk I/O: provides I/O statistics for requested storage devices
module("vicious.widgets.dio")
-- Initialize function tables
local disk_usage = {}
local disk_stats = {}
local disk_time = 0
-- Constant definitions
local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 }
-- {{{ Disk I/O widget type
local function worker(format)
local disk_lines = {}
for line in io.lines("/proc/diskstats") do
local device, read, write =
-- Linux kernel documentation: Documentation/iostats.txt
string.match(line, "([^%s]+) %d+ %d+ (%d+) %d+ %d+ %d+ (%d+)")
disk_lines[device] = { read, write }
end
local time = os.time()
local interval = os.difftime(time, disk_time)
if interval == 0 then interval = 1 end
for device, stats in pairs(disk_lines) do
-- Avoid insane values on startup
local last_stats = disk_stats[device] or stats
-- Check for overflows and counter resets (> 2^32)
if stats[1] < last_stats[1] or stats[2] < last_stats[2] then
last_stats[1], last_stats[2] = stats[1], stats[2]
end
-- Diskstats are absolute, substract our last reading
-- * divide by timediff because we don't know the timer value
local read = (stats[1] - last_stats[1]) / interval
local write = (stats[2] - last_stats[2]) / interval
-- Calculate and store I/O
helpers.uformat(disk_usage, device.." read", read, unit)
helpers.uformat(disk_usage, device.." write", write, unit)
helpers.uformat(disk_usage, device.." total", read + write, unit)
end
disk_time = time
disk_stats = disk_lines
return disk_usage
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,51 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- FS: provides file system disk space usage
module("vicious.widgets.fs")
-- Variable definitions
local unit = { ["mb"] = 1024, ["gb"] = 1024^2 }
-- {{{ Filesystem widget type
local function worker(format, warg)
-- Fallback to listing local filesystems
if warg then warg = "" else warg = "-l" end
local fs_info = {} -- Get data from df
local f = io.popen("LC_ALL=C df -kP " .. warg)
for line in f:lines() do -- Match: (size) (used)(avail)(use%) (mount)
local s = string.match(line, "^.-[%s]([%d]+)")
local u,a,p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%")
local m = string.match(line, "%%[%s]([%p%w]+)")
if u and m then -- Handle 1st line and broken regexp
helpers.uformat(fs_info, m .. " size", s, unit)
helpers.uformat(fs_info, m .. " used", u, unit)
helpers.uformat(fs_info, m .. " avail", a, unit)
fs_info["{" .. m .. " used_p}"] = tonumber(p)
fs_info["{" .. m .. " avail_p}"] = 100 - tonumber(p)
end
end
f:close()
return fs_info
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,82 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
find = string.find,
match = string.match
}
-- }}}
-- Gmail: provides count of new and subject of last e-mail on Gmail
module("vicious.widgets.gmail")
-- {{{ Variable definitions
local rss = {
inbox = {
"https://mail.google.com/mail/feed/atom",
"Gmail %- Inbox"
},
unread = {
"https://mail.google.com/mail/feed/atom/unread",
"Gmail %- Label"
},
--labelname = {
-- "https://mail.google.com/mail/feed/atom/labelname",
-- "Gmail %- Label"
--},
}
-- Default is just Inbox
local feed = rss.inbox
local mail = {
["{count}"] = 0,
["{subject}"] = "N/A"
}
-- }}}
-- {{{ Gmail widget type
local function worker(format, warg)
-- Get info from the Gmail atom feed
local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed[1])
-- Could be huge don't read it all at once, info we are after is at the top
for line in f:lines() do
mail["{count}"] = -- Count comes before messages and matches at least 0
tonumber(string.match(line, "<fullcount>([%d]+)</fullcount>")) or mail["{count}"]
-- Find subject tags
local title = string.match(line, "<title>(.*)</title>")
-- If the subject changed then break out of the loop
if title ~= nil and not string.find(title, feed[2]) then
-- Check if we should scroll, or maybe truncate
if warg then
if type(warg) == "table" then
title = helpers.scroll(title, warg[1], warg[2])
else
title = helpers.truncate(title, warg)
end
end
-- Spam sanitize the subject and store
mail["{subject}"] = helpers.escape(title)
break
end
end
f:close()
return mail
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,37 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
-- }}}
-- Hddtemp: provides hard drive temperatures using the hddtemp daemon
module("vicious.widgets.hddtemp")
-- {{{ HDD Temperature widget type
local function worker(format, warg)
-- Fallback to default hddtemp port
if warg == nil then warg = 7634 end
local hdd_temp = {} -- Get info from the hddtemp daemon
local f = io.popen("curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..warg)
for line in f:lines() do
for d, t in string.gmatch(line, "|([%/%a%d]+)|.-|([%d]+)|[CF]+|") do
hdd_temp["{"..d.."}"] = tonumber(t)
end
end
f:close()
return hdd_temp
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,17 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Setup environment
local setmetatable = setmetatable
local wrequire = require("vicious.helpers").wrequire
-- Vicious: widgets for the awesome window manager
module("vicious.widgets")
-- }}}
-- Load modules at runtime as needed
setmetatable(_M, { __index = wrequire })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local io = { open = io.open }
local setmetatable = setmetatable
local string = { gfind = string.gfind }
local helpers = require("vicious.helpers")
-- }}}
-- Mbox: provides the subject of last e-mail in a mbox file
module("vicious.widgets.mbox")
-- Initialize variables
local subject = "N/A"
-- {{{ Mailbox widget type
local function worker(format, warg)
if not warg then return end
-- mbox could be huge, get a 30kb chunk from EOF
if type(warg) ~= "table" then mbox = warg end
-- * attachment could be much bigger than 30kb
local f = io.open(mbox or warg[1])
f:seek("end", -30720)
local txt = f:read("*all")
f:close()
-- Find all Subject lines
for i in string.gfind(txt, "Subject: ([^\n]*)") do
subject = i
end
-- Check if we should scroll, or maybe truncate
if type(warg) == "table" then
if warg[3] ~= nil then
subject = helpers.scroll(subject, warg[2], warg[3])
else
subject = helpers.truncate(subject, warg[2])
end
end
return {helpers.escape(subject)}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,57 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { open = io.open }
local setmetatable = setmetatable
local string = { find = string.find }
-- }}}
-- Mboxc: provides the count of total, old and new messages in mbox files
module("vicious.widgets.mboxc")
-- {{{ Mbox count widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local count = { old = 0, total = 0, new = 0 }
-- Get data from mbox files
for i=1, #warg do
local f = io.open(warg[i])
while true do
-- Read the mbox line by line, if we are going to read
-- some *HUGE* folders then switch to reading chunks
local lines = f:read("*line")
if not lines then break end
-- Find all messages
-- * http://www.jwz.org/doc/content-length.html
local _, from = string.find(lines, "^From[%s]")
if from ~= nil then count.total = count.total + 1 end
-- Read messages have the Status header
local _, status = string.find(lines, "^Status:[%s]RO$")
if status ~= nil then count.old = count.old + 1 end
-- Skip the folder internal data
local _, int = string.find(lines, "^Subject:[%s].*FOLDER[%s]INTERNAL[%s]DATA")
if int ~= nil then count.total = count.total - 1 end
end
f:close()
end
-- Substract total from old to get the new count
count.new = count.total - count.old
return {count.total, count.old, count.new}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,40 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) Maildir Biff Widget, Fredrik Ax
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
-- }}}
-- Mdir: provides the number of new and unread messages in Maildir structures/dirs
module("vicious.widgets.mdir")
-- {{{ Maildir widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local count = { new = 0, cur = 0 }
for i=1, #warg do
-- Recursively find new messages
local f = io.popen("find "..warg[i].." -type f -wholename '*/new/*'")
for line in f:lines() do count.new = count.new + 1 end
f:close()
-- Recursively find "old" messages lacking the Seen flag
local f = io.popen("find "..warg[i].." -type f -regex '.*/cur/.*2,[^S]*$'")
for line in f:lines() do count.cur = count.cur + 1 end
f:close()
end
return {count.new, count.cur}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,49 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { gmatch = string.gmatch }
-- }}}
-- Mem: provides RAM and Swap usage statistics
module("vicious.widgets.mem")
-- {{{ Memory widget type
local function worker(format)
local mem = { buf = {}, swp = {} }
-- Get MEM info
for line in io.lines("/proc/meminfo") do
for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+") do
if k == "MemTotal" then mem.total = math.floor(v/1024)
elseif k == "MemFree" then mem.buf.f = math.floor(v/1024)
elseif k == "Buffers" then mem.buf.b = math.floor(v/1024)
elseif k == "Cached" then mem.buf.c = math.floor(v/1024)
elseif k == "SwapTotal" then mem.swp.t = math.floor(v/1024)
elseif k == "SwapFree" then mem.swp.f = math.floor(v/1024)
end
end
end
-- Calculate memory percentage
mem.free = mem.buf.f + mem.buf.b + mem.buf.c
mem.inuse = mem.total - mem.free
mem.usep = math.floor(mem.inuse / mem.total * 100)
-- Calculate swap percentage
mem.swp.inuse = mem.swp.t - mem.swp.f
mem.swp.usep = math.floor(mem.swp.inuse / mem.swp.t * 100)
return {mem.usep, mem.inuse, mem.total, mem.free,
mem.swp.usep, mem.swp.inuse, mem.swp.t, mem.swp.f}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,63 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Mpd: provides Music Player Daemon information
module("vicious.widgets.mpd")
-- {{{ MPD widget type
local function worker(format, warg)
local mpd_state = {
["{volume}"] = 0,
["{state}"] = "N/A",
["{Artist}"] = "N/A",
["{Title}"] = "N/A",
["{Album}"] = "N/A",
["{Genre}"] = "N/A",
--["{Name}"] = "N/A",
--["{file}"] = "N/A",
}
-- Fallback to MPD defaults
local pass = warg and (warg.password or warg[1]) or "\"\""
local host = warg and (warg.host or warg[2]) or "127.0.0.1"
local port = warg and (warg.port or warg[3]) or "6600"
-- Construct MPD client options
local mpdh = "telnet://"..host..":"..port
local echo = "echo 'password "..pass.."\nstatus\ncurrentsong\nclose'"
-- Get data from MPD server
local f = io.popen(echo.." | curl --connect-timeout 1 -fsm 3 "..mpdh)
for line in f:lines() do
for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
if k == "volume" then mpd_state["{"..k.."}"] = v and tonumber(v)
elseif k == "state" then mpd_state["{"..k.."}"] = helpers.capitalize(v)
elseif k == "Artist" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Title" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Album" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Genre" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "Name" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "file" then mpd_state["{"..k.."}"] = helpers.escape(v)
end
end
end
f:close()
return mpd_state
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,79 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local os = { time = os.time }
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Net: provides state and usage statistics of all network interfaces
module("vicious.widgets.net")
-- Initialize function tables
local nets = {}
-- Variable definitions
local unit = { ["b"] = 1, ["kb"] = 1024,
["mb"] = 1024^2, ["gb"] = 1024^3
}
-- {{{ Net widget type
local function worker(format)
local args = {}
-- Get NET stats
for line in io.lines("/proc/net/dev") do
-- Match wmaster0 as well as rt0 (multiple leading spaces)
local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):")
if name ~= nil then
-- Received bytes, first value after the name
local recv = tonumber(string.match(line, ":[%s]*([%d]+)"))
-- Transmited bytes, 7 fields from end of the line
local send = tonumber(string.match(line,
"([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$"))
helpers.uformat(args, name .. " rx", recv, unit)
helpers.uformat(args, name .. " tx", send, unit)
-- Operational state and carrier detection
local sysnet = helpers.pathtotable("/sys/class/net/" .. name)
args["{"..name.." carrier}"] = tonumber(sysnet.carrier) or 0
if nets[name] == nil then
-- Default values on the first run
nets[name] = {}
helpers.uformat(args, name .. " down", 0, unit)
helpers.uformat(args, name .. " up", 0, unit)
nets[name].time = os.time()
else -- Net stats are absolute, substract our last reading
local interval = os.time() - nets[name].time > 0 and
os.time() - nets[name].time or 1
nets[name].time = os.time()
local down = (recv - nets[name][1]) / interval
local up = (send - nets[name][2]) / interval
helpers.uformat(args, name .. " down", down, unit)
helpers.uformat(args, name .. " up", up, unit)
end
-- Store totals
nets[name][1] = recv
nets[name][2] = send
end
end
return args
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,61 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) org-awesome, Damien Leone
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { find = string.find }
local os = {
time = os.time,
date = os.date
}
-- }}}
-- Org: provides agenda statistics for Emacs org-mode
module("vicious.widgets.org")
-- {{{ OrgMode widget type
local function worker(format, warg)
if not warg then return end
-- Compute delays
local today = os.time{ year=os.date("%Y"), month=os.date("%m"), day=os.date("%d") }
local soon = today + 24 * 3600 * 3 -- 3 days ahead is close
local future = today + 24 * 3600 * 7 -- 7 days ahead is maximum
-- Initialize counters
local count = { past = 0, today = 0, soon = 0, future = 0 }
-- Get data from agenda files
for i=1, #warg do
for line in io.lines(warg[i]) do
local scheduled = string.find(line, "SCHEDULED:")
local closed = string.find(line, "CLOSED:")
local deadline = string.find(line, "DEADLINE:")
if (scheduled and not closed) or (deadline and not closed) then
local b, e, y, m, d = string.find(line, "(%d%d%d%d)-(%d%d)-(%d%d)")
if b then
local t = os.time{ year = y, month = m, day = d }
if t < today then count.past = count.past + 1
elseif t == today then count.today = count.today + 1
elseif t <= soon then count.soon = count.soon + 1
elseif t <= future then count.future = count.future + 1
end
end
end
end
end
return {count.past, count.today, count.soon, count.future}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,72 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local tonumber = tonumber
local io = { popen = io.popen }
local os = { getenv = os.getenv }
local math = { ceil = math.ceil }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
gsub = string.gsub,
match = string.match
}
-- }}}
-- OS: provides operating system information
module("vicious.widgets.os")
-- {{{ Operating system widget type
local function worker(format)
local system = {
["ostype"] = "N/A",
["hostname"] = "N/A",
["osrelease"] = "N/A",
["username"] = "N/A",
["entropy"] = "N/A",
["entropy_p"] = "N/A"
}
-- Linux manual page: uname(2)
local kernel = helpers.pathtotable("/proc/sys/kernel")
for k, v in pairs(system) do
if kernel[k] then
system[k] = string.gsub(kernel[k], "[%s]*$", "")
end
end
-- BSD manual page: uname(1)
if system["ostype"] == "N/A" then
local f = io.popen("uname -snr")
local uname = f:read("*line")
f:close()
system["ostype"], system["hostname"], system["osrelease"] =
string.match(uname, "([%w]+)[%s]([%w%p]+)[%s]([%w%p]+)")
end
-- Linux manual page: random(4)
if kernel.random then
-- Linux 2.6 default entropy pool is 4096-bits
local poolsize = tonumber(kernel.random.poolsize)
-- Get available entropy and calculate percentage
system["entropy"] = tonumber(kernel.random.entropy_avail)
system["entropy_p"] = math.ceil(system["entropy"] * 100 / poolsize)
end
-- Get user from the environment
system["username"] = os.getenv("USER")
return {system["ostype"], system["osrelease"], system["username"],
system["hostname"], system["entropy"], system["entropy_p"]}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,46 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local math = { max = math.max }
local setmetatable = setmetatable
-- }}}
-- Pkg: provides number of pending updates on UNIX systems
module("vicious.widgets.pkg")
-- {{{ Packages widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local updates = 0
local manager = {
["Arch"] = { cmd = "pacman -Qu" },
["Arch S"] = { cmd = "yes | pacman -Sup", sub = 2 },
["Debian"] = { cmd = "apt-show-versions -u -b" },
["Ubuntu"] = { cmd = "aptitude search '~U'" },
["Fedora"] = { cmd = "yum list updates", sub = 3 },
["FreeBSD"] ={ cmd = "pkg_version -I -l '<'" },
["Mandriva"]={ cmd = "urpmq --auto-select" }
}
-- Check if updates are available
local pkg = manager[warg]
local f = io.popen(pkg.cmd)
for line in f:lines() do
updates = updates + 1
end
f:close()
return {pkg.sub and math.max(updates-pkg.sub, 0) or updates}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,57 @@
-----------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Hagen Schink <troja84@googlemail.com>
-----------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = {
len = string.len,
sub = string.sub,
match = string.match,
gmatch = string.gmatch
}
-- }}}
-- Raid: provides state information for a requested RAID array
module("vicious.widgets.raid")
-- Initialize function tables
local mddev = {}
-- {{{ RAID widget type
local function worker(format, warg)
if not warg then return end
mddev[warg] = {
["found"] = false,
["active"] = 0,
["assigned"] = 0
}
-- Linux manual page: md(4)
for line in io.lines("/proc/mdstat") do
if mddev[warg]["found"] then
local updev = string.match(line, "%[[_U]+%]")
for i in string.gmatch(updev, "U") do
mddev[warg]["active"] = mddev[warg]["active"] + 1
end
break
elseif string.sub(line, 1, string.len(warg)) == warg then
mddev[warg]["found"] = true
for i in string.gmatch(line, "%[[%d]%]") do
mddev[warg]["assigned"] = mddev[warg]["assigned"] + 1
end
end
end
return {mddev[warg]["assigned"], mddev[warg]["active"]}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,45 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Thermal: provides temperature levels of ACPI and coretemp thermal zones
module("vicious.widgets.thermal")
-- {{{ Thermal widget type
local function worker(format, warg)
if not warg then return end
local zone = { -- Known temperature data sources
["sys"] = {"/sys/class/thermal/", file = "temp", div = 1000},
["core"] = {"/sys/devices/platform/", file = "temp1_input",div = 1000},
["proc"] = {"/proc/acpi/thermal_zone/",file = "temperature"}
} -- Default to /sys/class/thermal
warg = type(warg) == "table" and warg or { warg, "sys" }
-- Get temperature from thermal zone
local thermal = helpers.pathtotable(zone[warg[2]][1] .. warg[1])
if thermal[zone[warg[2]].file] then
if zone[warg[2]].div then
return {thermal[zone[warg[2]].file] / zone[warg[2]].div}
else -- /proc/acpi "temperature: N C"
return {tonumber(string.match(thermal[zone[warg[2]].file], "[%d]+"))}
end
end
return {0}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,35 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Uptime: provides system uptime and load information
module("vicious.widgets.uptime")
-- {{{ Uptime widget type
local function worker(format)
local proc = helpers.pathtotable("/proc")
-- Get system uptime
local up_t = math.floor(string.match(proc.uptime, "[%d]+"))
local up_d = math.floor(up_t / (3600 * 24))
local up_h = math.floor((up_t % (3600 * 24)) / 3600)
local up_m = math.floor(((up_t % (3600 * 24)) % 3600) / 60)
local l1, l5, l15 = -- Get load averages for past 1, 5 and 15 minutes
string.match(proc.loadavg, "([%d%.]+)[%s]([%d%.]+)[%s]([%d%.]+)")
return {up_d, up_h, up_m, l1, l5, l15}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
-- }}}
-- Volume: provides volume levels and state of requested ALSA mixers
module("vicious.widgets.volume")
-- {{{ Volume widget type
local function worker(format, warg)
if not warg then return end
local mixer_state = {
["on"] = "", -- "",
["off"] = "" -- "M"
}
-- Get mixer control contents
local f = io.popen("amixer get " .. warg)
local mixer = f:read("*all")
f:close()
-- Capture mixer control state: [5%] ... ... [on]
local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
-- Handle mixers without data
if volu == nil then
return {0, mixer_state["off"]}
end
-- Handle mixers without mute
if mute == "" and volu == "0"
-- Handle mixers that are muted
or mute == "off" then
mute = mixer_state["off"]
else
mute = mixer_state["on"]
end
return {tonumber(volu), mute}
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,85 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local math = { ceil = math.ceil }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Weather: provides weather information for a requested station
module("vicious.widgets.weather")
-- Initialize function tables
local weather = {
["{city}"] = "N/A",
["{wind}"] = "N/A",
["{windmph}"] = "N/A",
["{windkmh}"] = "N/A",
["{sky}"] = "N/A",
["{weather}"] = "N/A",
["{tempf}"] = "N/A",
["{tempc}"] = "N/A",
["{humid}"] = "N/A",
["{press}"] = "N/A"
}
-- {{{ Weather widget type
local function worker(format, warg)
if not warg then return end
-- Get weather forceast by the station ICAO code, from:
-- * US National Oceanic and Atmospheric Administration
local noaa = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"
local f = io.popen("curl --connect-timeout 1 -fsm 3 "..noaa..warg..".TXT")
local ws = f:read("*all")
f:close()
-- Check if there was a timeout or a problem with the station
if ws == nil then return weather end
weather["{city}"] = -- City and/or area
string.match(ws, "^(.+)%,.*%([%u]+%)") or weather["{city}"]
weather["{wind}"] = -- Wind direction and degrees if available
string.match(ws, "Wind:[%s][%a]+[%s][%a]+[%s](.+)[%s]at.+$") or weather["{wind}"]
weather["{windmph}"] = -- Wind speed in MPH if available
string.match(ws, "Wind:[%s].+[%s]at[%s]([%d]+)[%s]MPH") or weather["{windmph}"]
weather["{sky}"] = -- Sky conditions if available
string.match(ws, "Sky[%s]conditions:[%s](.-)[%c]") or weather["{sky}"]
weather["{weather}"] = -- Weather conditions if available
string.match(ws, "Weather:[%s](.-)[%c]") or weather["{weather}"]
weather["{tempf}"] = -- Temperature in fahrenheit
string.match(ws, "Temperature:[%s]([%-]?[%d%.]+).*[%c]") or weather["{tempf}"]
weather["{humid}"] = -- Relative humidity in percent
string.match(ws, "Relative[%s]Humidity:[%s]([%d]+)%%") or weather["{humid}"]
weather["{press}"] = -- Pressure in hPa
string.match(ws, "Pressure[%s].+%((.+)[%s]hPa%)") or weather["{press}"]
-- Wind speed in km/h if MPH was available
if weather["{windmph}"] ~= "N/A" then
weather["{windmph}"] = tonumber(weather["{windmph}"])
weather["{windkmh}"] = math.ceil(weather["{windmph}"] * 1.6)
end -- Temperature in °C if °F was available
if weather["{tempf}"] ~= "N/A" then
weather["{tempf}"] = tonumber(weather["{tempf}"])
weather["{tempc}"] = math.ceil((weather["{tempf}"] - 32) * 5/9)
end -- Capitalize some stats so they don't look so out of place
if weather["{sky}"] ~= "N/A" then
weather["{sky}"] = helpers.capitalize(weather["{sky}"])
end
if weather["{weather}"] ~= "N/A" then
weather["{weather}"] = helpers.capitalize(weather["{weather}"])
end
return weather
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,80 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local math = { ceil = math.ceil }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local io = {
open = io.open,
popen = io.popen
}
local string = {
find = string.find,
match = string.match
}
-- }}}
-- Wifi: provides wireless information for a requested interface
module("vicious.widgets.wifi")
-- {{{ Wireless widget type
local function worker(format, warg)
if not warg then return end
-- Default values
local winfo = {
["{ssid}"] = "N/A",
["{mode}"] = "N/A",
["{chan}"] = 0,
["{rate}"] = 0,
["{link}"] = 0,
["{linp}"] = 0,
["{sign}"] = 0
}
-- Get data from iwconfig where available
local iwconfig = "/sbin/iwconfig"
local f = io.open(iwconfig, "rb")
if not f then
iwconfig = "/usr/sbin/iwconfig"
else
f:close()
end
local f = io.popen(iwconfig .." ".. warg .. " 2>&1")
local iw = f:read("*all")
f:close()
-- iwconfig wasn't found, isn't executable, or non-wireless interface
if iw == nil or string.find(iw, "No such device") then
return winfo
end
-- Output differs from system to system, some stats can be
-- separated by =, and not all drivers report all stats
winfo["{ssid}"] = -- SSID can have almost anything in it
helpers.escape(string.match(iw, 'ESSID[=:]"(.-)"') or winfo["{ssid}"])
winfo["{mode}"] = -- Modes are simple, but also match the "-" in Ad-Hoc
string.match(iw, "Mode[=:]([%w%-]*)") or winfo["{mode}"]
winfo["{chan}"] = -- Channels are plain digits
tonumber(string.match(iw, "Channel[=:]([%d]+)") or winfo["{chan}"])
winfo["{rate}"] = -- Bitrate can start with a space, we don't want to display Mb/s
tonumber(string.match(iw, "Bit Rate[=:]([%s]?[%d%.]*)") or winfo["{rate}"])
winfo["{link}"] = -- Link quality can contain a slash (32/70), match only the first number
tonumber(string.match(iw, "Link Quality[=:]([%d]+)") or winfo["{link}"])
winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation
tonumber(string.match(iw, "Signal level[=:]([%-]?[%d]+)") or winfo["{sign}"])
-- Link quality percentage if quality was available
if winfo["{link}"] ~= 0 then winfo["{linp}"] = math.ceil(winfo["{link}"] / 0.7) end
return winfo
end
-- }}}
setmetatable(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,154 @@
menu98edb85b00d9527ad5acebe451b3fae6 = { {"7-Zip FM", "7zFM"},
{"Android Notifier Desktop", "/usr/share/android-notifier-desktop/run.sh"},
{"Archive Manager", "file-roller "},
{"AutoKey (GTK)", "autokey-gtk"},
{"Avant Window Navigator", "avant-window-navigator"},
{"ClamTk", "clamtk "},
{"Disk Utility", "palimpsest"},
{"File Manager", "pcmanfm "},
{"HP Device Manager", "hp-toolbox"},
{"Image Viewer", "gpicview "},
{"KeePassX", "keepassx "},
{"LXTerminal", "lxterminal"},
{"Leafpad", "leafpad "},
{"NFO Viewer", "nfoview "},
{"Root Terminal", "gksu -l gnome-terminal"},
{"Terminator", "terminator"},
{"Vi IMproved", "gvim -f "},
}
menu78059f1898ed518c6ccd6a6392fa82c1 = { {"AUR", "xdg-open https://aur.archlinux.org"},
{"Bugs", "xdg-open https://bugs.archlinux.org"},
{"Developers", "xdg-open http://www.archlinux.org/developers/"},
{"Documentation", "xdg-open https://wiki.archlinux.org/index.php/Official_Arch_Linux_Install_Guide"},
{"Donate", "xdg-open http://www.archlinux.org/donate/"},
{"Forum", "xdg-open https://bbs.archlinux.org"},
{"Homepage", "xdg-open http://www.archlinux.org"},
{"SVN", "xdg-open http://projects.archlinux.org/svntogit/"},
{"Schwag", "xdg-open http://www.zazzle.com/archlinux/"},
{"Wiki", "xdg-open https://wiki.archlinux.org"},
}
menu251bd8143891238ecedc306508e29017 = { {"Imprudence Second Life viewer", "/usr/bin/imprudence-secondlife"},
{"Lincity-NG", "lincity-ng"},
{"Minecraft", "minecraft"},
{"PlayOnLinux", "playonlinux"},
{"Savage 2", "/usr/bin/savage2"},
{"Savage 2 Map Editor", "/usr/bin/savage2 \"PushMod editor; Set host_autoExec StartClient\""},
{"Savage 2 Model Viewer", "/usr/bin/savage2 \"PushMod modelviewer; Set host_autoExec StartClient\""},
{"Supertuxkart", "/usr/bin/supertuxkart --log=file"},
{"The Lord of the Rings Online", "pylotro"},
{"World of Padman", "worldofpadman"},
{"fretsonfire", "fretsonfire"},
}
menud334dfcea59127bedfcdbe0a3ee7f494 = { {"E-book Viewer", "ebook-viewer "},
{"GNU Image Manipulation Program", "gimp-2.7 "},
{"Image Viewer", "gpicview "},
{"LRF Viewer", "lrfviewer "},
{"XSane - Scanning", "xsane"},
}
menuc8205c7636e728d448c2774e6a4a944b = { {"Avahi SSH Server Browser", "/usr/bin/bssh"},
{"Avahi VNC Server Browser", "/usr/bin/bvnc"},
{"Chromium", "chromium "},
{"Dropbox", "/opt/dropbox/dropboxd"},
{"FileZilla", "filezilla"},
{"Firefox", "firefox "},
{"Links", "xlinks -g"},
{"Mangler", "mangler"},
{"Midori", "midori "},
{"Opera", "/usr/bin/opera "},
{"Pidgin Internet Messenger", "pidgin"},
{"QuickSynergy", "quicksynergy"},
{"SeaMonkey internet suite", "seamonkey "},
{"Skype", "skype"},
{"TeamSpeak 3", "/usr/bin/teamspeak3"},
{"TeamViewer", "/opt/teamviewer/teamviewer/6/bin/teamviewer"},
{"Thunderbird", "thunderbird "},
{"Turpial", "turpial"},
{"Wicd", "/usr/bin/wicd-client"},
{"X11VNC Server", "x11vnc -gui tray=setpass -rfbport PROMPT -bg -o %HOME/.x11vnc.log.%VNCDISPLAY"},
}
menudf814135652a5a308fea15bff37ea284 = { {"Calibre", "calibre"},
{"LibreOffice ", "libreoffice "},
{"ePDFViewer", "epdfview "},
}
menu6311ae17c1ee52b36e68aaf4ad066387 = { {"ROX Filer", "rox"},
{"dosbox Emulator", "dosbox"},
}
menue6f43c40ab1c07cd29e4e83e4ef6bf85 = { {"Android SDK", "android"},
{"BlueJ", "/usr/bin/bluej"},
{"Bluefish Editor", "bluefish "},
{"Eclipse", "eclipse"},
{"Google Gadget Designer", "/usr/bin/ggl-gtk -sa -nd -gp /usr/share/google-gadgets/designer.gg"},
{"Java Monitoring and Management Console", "jconsole"},
{"Java VisualVM", "jvisualvm"},
{"MonoDevelop", "monodevelop "},
{"NetBeans IDE", "/usr/share/netbeans/bin/netbeans"},
{"Qt Assistant", "/usr/bin/assistant"},
{"Qt Designer", "/usr/bin/designer"},
{"Qt Linguist", "/usr/bin/linguist"},
}
menu52dd1c847264a75f400961bfb4d1c849 = { {"AcetoneISO", "acetoneiso "},
{"Audacious", "audacious "},
{"Audacity", "audacity"},
{"EasyTAG", "easytag "},
{"Ex Falso", "exfalso"},
{"GNOME MPlayer", "gnome-mplayer "},
{"Gnome Music Player Client", "gmpc"},
{"HandBrake", "ghb"},
{"Last.fm", "lastfm"},
{"Nero Linux", "nero "},
{"Nero Linux Express", "neroexpress "},
{"Pithos", "pithos"},
{"QT V4L2 test Utility", "qv4l2"},
{"Quod Libet", "quodlibet"},
{"Sonata", "sonata"},
{"Sound Converter", "soundconverter "},
{"VLC media player", "vlc "},
{"VolWheel", "volwheel"},
{"Webcam Application", "wxcam"},
{"XBMC Media Center", "xbmc"},
{"dvd::rip", "/usr/bin/vendor_perl/dvdrip"},
{"gtk-recordMyDesktop", "gtk-recordMyDesktop"},
}
menuee69799670a33f75d45c57d1d1cd0ab3 = { {"Avahi Zeroconf Browser", "/usr/bin/avahi-discover"},
{"Bulk Rename", "/usr/lib/Thunar/ThunarBulkRename "},
{"Cairo Composite Manager", "cairo-compmgr"},
{"Cairo-Dock (no OpenGL)", "cairo-dock -c"},
{"Compiz Fusion Icon", "fusion-icon"},
{"GLX-Dock (Cairo-Dock with OpenGL)", "cairo-dock -o"},
{"GParted", "gksu /usr/sbin/gparted "},
{"Htop", "xterm -e htop"},
{"Manage Printing", "/usr/bin/xdg-open http://localhost:631/"},
{"Oracle VM VirtualBox", "VirtualBox "},
{"PkgBrowser", "pkgbrowser"},
{"Sakura", "sakura"},
{"Terminal", "Terminal"},
{"Thunar File Manager", "Thunar "},
{"UNetbootin", "/usr/bin/unetbootin"},
{"VMware Player", "/usr/bin/vmplayer"},
{"VMware Workstation", "/usr/bin/vmware"},
{"Virtual Network Editor", "/usr/bin/vmware-netcfg"},
{"Wireshark", "wireshark"},
{"rxvt-unicode", "urxvt"},
}
xdgmenu = { {"Accessories", menu98edb85b00d9527ad5acebe451b3fae6},
{"Archlinux", menu78059f1898ed518c6ccd6a6392fa82c1},
{"Games", menu251bd8143891238ecedc306508e29017},
{"Graphics", menud334dfcea59127bedfcdbe0a3ee7f494},
{"Internet", menuc8205c7636e728d448c2774e6a4a944b},
{"Office", menudf814135652a5a308fea15bff37ea284},
{"Other", menu6311ae17c1ee52b36e68aaf4ad066387},
{"Programming", menue6f43c40ab1c07cd29e4e83e4ef6bf85},
{"Sound & Video", menu52dd1c847264a75f400961bfb4d1c849},
{"System Tools", menuee69799670a33f75d45c57d1d1cd0ab3},
}

Some files were not shown because too many files have changed in this diff Show More