Repository: dluksza/screenful Branch: master Commit: dd98a44b73e9 Files: 7 Total size: 10.9 KB Directory structure: gitextract_xrrk0h17/ ├── .gitignore ├── 98-screen-detect.rules ├── LICENSE ├── README.md ├── notify-awesome ├── screenful.lua └── screens_db.lua ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.swp ================================================ FILE: 98-screen-detect.rules ================================================ # change /devices/pci0000:00/0000:00:02.0/drm/card0 (drm) ACTION=="change", SUBSYSTEM=="drm", RUN+="notify-awesome %k" ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2012 Dariusz Łuksza Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ screenful ========= screenful is an extension library for [Awesome WM](http://awesome.naquadah.org/) that enables it to automatically setup screen organization. It leverage from udev notification about drm change events and device specific EDID information. When drm change event occurs, screenful is informed via awesome-client command. Then it discovers with output was connected and reads screen EDID. Next it reads lua configuration script and looks for configuration for this device. If it isn't available yet, it will add commented out template and run default configuration, otherwise it will execute specific configuration. Since configuration file is also lua script you can do many things when screens are connected or disconnected. For example you can change default mplayer audio output when you are connecting HD TV set using HDMI; theoretically you can also reorganize yours windows/tags etc. When one of configuration options is missing ('connected' or 'disconnected') then default action will be launched. Install ======= * 98-screen-detect.rules - copy to /etc/udev/rules.d/ and execute with root privileges `udevadm control --reload-rules` * notify-awesome - copy to /lib/udev and add execution bit * screenful.lua - copy to ~/.config/awesome * screens_db.lua - copy to ~/.config/awesome Add to your rc.lua file the following require statements: ``` require("awful.remote") require("screenful") ``` * Restart Awesome WM Now you can connect an additional screen. HDMI outputs are detected almost instantly, but in the case of VGA outputs you need to wait a couple of seconds for detection. Default configuration will clone LVDS1 output. Then you can edit ~/.config/awesome/screens_db.lua config file. At the end you will find commented out configuration template with screen EDID value. Both functions ('connected' and 'disconnected') should return xrandr options eg: ```lua screens = { ['default'] = { ['connected'] = function (xrandrOutput) return '--output ' .. xrandrOutput .. ' --auto --same-as LVDS1' end, ['disconnected'] = function (xrandrOutput) return '--output ' .. xrandrOutput .. ' --off --output LVDS1 --auto' end } ['99999999999'] = { ['connected'] = function (xrandrOutput) return '--output ' .. xrandrOutput .. ' --auto --above LVDS1' end, } } ``` In this example, when screen with ID 99999999999 is connected to VGA1 output then screenful will execute command: ``` $ xrandr --output VGA1 --auto --above LVDS1 ``` When it is disconnected, because 'disconnected' is not defined for this output, default disconnect action will be executed: ``` $ xrandr --output VGA1 --off --output LVDS1 --auto ``` Known BUGS ========= * when device is disconnected always default disconnect action is called TODO ==== * setup proper screen organization on awesome boot * support more then one card * support conditional configuration based on connected devices/outputs and id's ================================================ FILE: notify-awesome ================================================ #!/bin/sh _PID=$(pgrep -x awesome) _UID=$(ps -o uid= -p $_PID) USER=$(id -nu $_UID) DBUS_ADDRESS_VAR=$(cat /proc/$_PID/environ | grep -z "^DBUS_SESSION_BUS_ADDRESS=") notify() { su - $USER -c "/bin/bash \ -c ' \ export DISPLAY=:0; \ export XAUTHORITY='/home/$USER/.Xauthority'; \ export $DBUS_ADDRESS_VAR; \ dbus-send --dest=org.awesomewm.awful --type=method_call \ / org.awesomewm.awful.Remote.Eval string:"updateScreens\\\(\\\"$1\\\"\\\)" \ ' \ " } notify $1 & ================================================ FILE: screenful.lua ================================================ ---------------------------------------------------------------------------- ---- @author dluksza <dariusz@luksza.org> ---- @copyright 2012 dluksza ------------------------------------------------------------------------------ -- Package envronment local naughty = require('naughty') local awful = require("awful") local screen = require("awful.screen") local io = require("io") local os = require("os") local awesome = awesome require('screens_db') local card = 'card0' local dev = '/sys/class/drm/' local configPath = awful.util.getdir("config") .. "/screens_db.lua" local function log(text) naughty.notify({ title = 'screenful debug', text = text, ontop = true, preset = naughty.config.presets.critical }) local log = io.open('/tmp/awesomewm-widget-screenful.error.log', 'a+') log:write(text .. "\n") log:flush() log:close() end local function isOutputConnected(path) local status = io.open(path .. '/status', 'r') local value = status:read('*all') return 'connected\n' == value end local function connectedOutputs(path, card) local result = {} local outputs = io.popen('ls -1 -d ' .. path .. '/' .. card .. '-*') while true do local output = outputs:read('*line') if not output then break end if isOutputConnected(output) then result[output] = true end end return result end local function emptyStr(str) return str == nil or str == '' end local function getScreenId(output) local screenId = nil if isOutputConnected(output) then screenId = '' os.execute("udevadm settle") local edid = io.open(output .. '/edid', 'rb') local id = edid:read('*all') io.close(edid) if emptyStr(id) then log('cannot read EDID from ' .. output .. '/edid') return false end for i = 12, 17 do code = id:byte(i) if code then screenId = screenId .. code end end end return screenId end local function getXrandrOutput(outputPath, outCard) local regex = dev .. outCard .. '/' .. outCard .. '[-]' local drmName = string.gsub(outputPath, regex, '') if outputMapping[drmName] then return outputMapping[drmName] end return drmName end local function mergeTables(table1, table2) local result = {} for k,v in pairs(table1) do result[k] = v end for k,v in pairs(table2) do result[k] = v end return result end local function hasConfigurationFor(screenId) local file = io.open(configPath, 'r') local conf = file:read('*all') file:close() return string.find(conf, "['\"]" .. screenId .. "['\"]") end local function appendConfiguration(screenId, xrandrOut) local file = io.open(configPath, 'a') file:write("--\t['" .. screenId .. "'] = { -- " .. xrandrOut .. "\n") file:write("--\t\t['connected'] = function (xrandrOutput)\n") file:write("--\t\t\tif xrandrOutput ~= defaultOutput then\n") file:write("--\t\t\t\treturn '--output ' .. xrandrOutput .. ' --auto --same-as ' .. defaultOutput\n") file:write("--\t\t\tend\n") file:write("--\t\t\treturn nil\n") file:write("--\t\tend,\n") file:write("--\t\t['disconnected'] = function (xrandrOutput)\n") file:write("--\t\t\tif xrandrOutput ~= defaultOutput then\n") file:write("--\t\t\treturn '--output ' .. xrandrOutput .. ' --off --output ' .. defaultOutput .. ' --auto'\n") file:write("--\t\t\tend\n") file:write("--\t\t\treturn nil\n") file:write("--\t\tend\n") file:write("--\t}\n") file:flush() file:close() end local function setupScreen(xrandrParams) os.execute('xrandr ' .. xrandrParams) end local function performConfiguredAction(screenId, action, xrandrOut) local xrandrOpts = '' if screenId then local configuration = screens[screenId] if configuration then if configuration[action] then -- get xrandr options xrandrOpts = configuration[action](xrandrOut) end else -- configuration not found, append configuration template if tostring(screenId):len() ~= 0 and not hasConfigurationFor(screenId) then naughty.notify({text = 'Append new configuration for screen id: ' .. screenId}) appendConfiguration(screenId, xrandrOut) end end end if xrandrOpts:len() == 0 then -- use default configuration if specific was not found xrandrOpts = screens['default'][action](xrandrOut) end if xrandrOpts then setupScreen(xrandrOpts) end end local function disableOutput(out, changedCard) local xrandrOut = getXrandrOutput(out, changedCard) local screenId = getScreenId(out) performConfiguredAction(screenId, 'disconnected', xrandrOut) naughty.notify({ text='Output ' .. xrandrOut .. ' disconnected' }) end local function enableOutput(out, changedCard) local xrandrOut = getXrandrOutput(out, changedCard) local screenId = getScreenId(out) performConfiguredAction(screenId, 'connected', xrandrOut) end local cardDev = dev .. card local outputs = connectedOutputs(cardDev, card) function updateScreens(changedCard) local newCardDev = dev .. changedCard local newOutputs = connectedOutputs(newCardDev, changedCard) local mergedOutputs = mergeTables(outputs, newOutputs) for out in pairs(mergedOutputs) do if not outputs[out] then -- connected enableOutput(out, changedCard) elseif not newOutputs[out] then -- disconnected disableOutput(out, changedCard) end end outputs = newOutputs -- reinit awesome awesome.restart() end ================================================ FILE: screens_db.lua ================================================ local naughty = require("naughty") local defaultOutput = 'eDP1' outputMapping = { ['DP-1'] = 'DP1', ['DP-2'] = 'DP2', ['DP-3'] = 'DP3', ['VGA-1'] = 'VGA1', ['LVDS-1'] = 'LVDS1', ['HDMI-A-1'] = 'HDMI1', ['HDMI-A-2'] = 'HDMI2', ['eDP-1'] = 'eDP1', ['eDP-2'] = 'eDP2', } screens = { ['default'] = { ['connected'] = function (xrandrOutput) if xrandrOutput ~= defaultOutput then return '--output ' .. xrandrOutput .. ' --auto --same-as ' .. defaultOutput end return nil end, ['disconnected'] = function (xrandrOutput) if xrandrOutput ~= defaultOutput then return '--output ' .. xrandrOutput .. ' --off --output ' .. defaultOutput .. ' --auto' end return nil end }, }