[
  {
    "path": ".gitignore",
    "content": "*.swp\n"
  },
  {
    "path": "98-screen-detect.rules",
    "content": "#  change   /devices/pci0000:00/0000:00:02.0/drm/card0 (drm)\nACTION==\"change\", SUBSYSTEM==\"drm\", RUN+=\"notify-awesome %k\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2012 Dariusz Łuksza\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "screenful\n=========\n\nscreenful 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.\n\nWhen 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.\n\nSince 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.\n\nInstall\n=======\n\n* 98-screen-detect.rules - copy to /etc/udev/rules.d/ and execute with root privileges `udevadm control --reload-rules`\n* notify-awesome - copy to /lib/udev and add execution bit\n* screenful.lua - copy to ~/.config/awesome\n* screens_db.lua - copy to ~/.config/awesome\n\nAdd to your rc.lua file the following require statements:\n```\nrequire(\"awful.remote\")\nrequire(\"screenful\")\n```\n\n* Restart Awesome WM\n\nNow 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:\n\n```lua\nscreens = {\n    ['default'] = {\n        ['connected'] = function (xrandrOutput)\n            return '--output ' .. xrandrOutput .. ' --auto --same-as LVDS1'\n        end,\n        ['disconnected'] = function (xrandrOutput)\n            return '--output ' .. xrandrOutput .. ' --off --output LVDS1 --auto'\n        end\n    }\n    ['99999999999'] = {\n        ['connected'] = function (xrandrOutput)\n            return '--output ' .. xrandrOutput .. ' --auto --above LVDS1'\n        end,\n    }\n}\n```\n\nIn this example, when screen with ID 99999999999 is connected to VGA1 output then screenful will execute command:\n\n```\n$ xrandr --output VGA1 --auto --above LVDS1\n```\n\nWhen it is disconnected, because 'disconnected' is not defined for this output, default disconnect action will be executed:\n\n```\n$ xrandr --output VGA1 --off --output LVDS1 --auto\n```\n\nKnown BUGS\n=========\n\n* when device is disconnected always default disconnect action is called\n\nTODO\n====\n\n* setup proper screen organization on awesome boot\n* support more then one card\n* support conditional configuration based on connected devices/outputs and id's\n"
  },
  {
    "path": "notify-awesome",
    "content": "#!/bin/sh\n\n_PID=$(pgrep -x awesome)\n_UID=$(ps -o uid= -p $_PID)\nUSER=$(id -nu $_UID)\nDBUS_ADDRESS_VAR=$(cat /proc/$_PID/environ | grep -z \"^DBUS_SESSION_BUS_ADDRESS=\")\n\nnotify() {\n  \tsu - $USER -c \"/bin/bash \\\n                    -c ' \\\n                        export DISPLAY=:0; \\\n                        export XAUTHORITY='/home/$USER/.Xauthority'; \\\n                        export $DBUS_ADDRESS_VAR; \\\n                        dbus-send --dest=org.awesomewm.awful --type=method_call \\\n                          / org.awesomewm.awful.Remote.Eval string:\"updateScreens\\\\\\(\\\\\\\"$1\\\\\\\"\\\\\\)\" \\\n                    ' \\\n                \"\n    }\n\nnotify $1 &\n"
  },
  {
    "path": "screenful.lua",
    "content": "----------------------------------------------------------------------------\n---- @author dluksza &lt;dariusz@luksza.org&gt;\n---- @copyright 2012 dluksza\n------------------------------------------------------------------------------\n\n-- Package envronment\nlocal naughty = require('naughty')\nlocal awful = require(\"awful\")\nlocal screen = require(\"awful.screen\")\nlocal io = require(\"io\")\nlocal os = require(\"os\")\nlocal awesome = awesome\nrequire('screens_db')\n\nlocal card = 'card0'\nlocal dev = '/sys/class/drm/'\nlocal configPath = awful.util.getdir(\"config\") .. \"/screens_db.lua\"\n\nlocal function log(text)\n\tnaughty.notify({\n\t\ttitle = 'screenful debug',\n\t\ttext = text,\n\t\tontop = true,\n\t\tpreset = naughty.config.presets.critical\n\t})\n\tlocal log = io.open('/tmp/awesomewm-widget-screenful.error.log', 'a+')\n\tlog:write(text .. \"\\n\")\n\tlog:flush()\n\tlog:close()\nend\n\nlocal function isOutputConnected(path)\n\tlocal status = io.open(path .. '/status', 'r')\n\tlocal value = status:read('*all')\n\n\treturn 'connected\\n' == value\nend\n\nlocal function connectedOutputs(path, card)\n\tlocal result = {}\n\tlocal outputs = io.popen('ls -1 -d ' .. path .. '/' .. card .. '-*')\n\twhile true do\n\t\tlocal output = outputs:read('*line')\n\t\tif not output then break end\n\t\tif isOutputConnected(output) then\n\t\t\tresult[output] = true\n\t\tend\n\tend\n\n\treturn result\nend\n\nlocal function emptyStr(str)\n\treturn str == nil or str == ''\nend\n\nlocal function getScreenId(output)\n\tlocal screenId = nil\n\n    if isOutputConnected(output) then\n\t\tscreenId = ''\n\t\tos.execute(\"udevadm settle\")\n\t\tlocal edid = io.open(output .. '/edid', 'rb')\n\t\tlocal id = edid:read('*all')\n        io.close(edid)\n\t\tif emptyStr(id) then\n\t\t\tlog('cannot read EDID from ' .. output .. '/edid')\n\t\t\treturn false\n        end\n        for i = 12, 17 do\n            code = id:byte(i)\n            if code then\n                screenId = screenId .. code\n            end\n        end\n    end\n\treturn screenId\nend\n\nlocal function getXrandrOutput(outputPath, outCard)\n\tlocal regex = dev .. outCard .. '/' .. outCard .. '[-]'\n\tlocal drmName = string.gsub(outputPath, regex, '')\n\n\tif outputMapping[drmName] then\n\t\treturn outputMapping[drmName]\n\tend\n\n\treturn drmName\nend\n\nlocal function mergeTables(table1, table2)\n\tlocal result = {}\n\tfor k,v in pairs(table1) do\n\t\tresult[k] = v\n\tend\n\tfor k,v in pairs(table2) do\n\t\tresult[k] = v\n\tend\n\n\treturn result\nend\n\nlocal function hasConfigurationFor(screenId)\n\tlocal file = io.open(configPath, 'r')\n\tlocal conf = file:read('*all')\n\tfile:close()\n\n\treturn string.find(conf, \"['\\\"]\" .. screenId .. \"['\\\"]\")\nend\n\nlocal function appendConfiguration(screenId, xrandrOut)\n\tlocal file = io.open(configPath, 'a')\n\n\tfile:write(\"--\\t['\" .. screenId .. \"'] = { -- \" .. xrandrOut .. \"\\n\")\n\tfile:write(\"--\\t\\t['connected'] = function (xrandrOutput)\\n\")\n\tfile:write(\"--\\t\\t\\tif xrandrOutput ~= defaultOutput then\\n\")\n\tfile:write(\"--\\t\\t\\t\\treturn '--output ' .. xrandrOutput .. ' --auto --same-as ' .. defaultOutput\\n\")\n\tfile:write(\"--\\t\\t\\tend\\n\")\n\tfile:write(\"--\\t\\t\\treturn nil\\n\")\n\tfile:write(\"--\\t\\tend,\\n\")\n\tfile:write(\"--\\t\\t['disconnected'] = function (xrandrOutput)\\n\")\n\tfile:write(\"--\\t\\t\\tif xrandrOutput ~= defaultOutput then\\n\")\n\tfile:write(\"--\\t\\t\\treturn '--output ' .. xrandrOutput .. ' --off --output ' .. defaultOutput .. ' --auto'\\n\")\n\tfile:write(\"--\\t\\t\\tend\\n\")\n\tfile:write(\"--\\t\\t\\treturn nil\\n\")\n\tfile:write(\"--\\t\\tend\\n\")\n\tfile:write(\"--\\t}\\n\")\n\tfile:flush()\n\tfile:close()\nend\n\nlocal function setupScreen(xrandrParams)\n\tos.execute('xrandr ' .. xrandrParams)\nend\n\nlocal function performConfiguredAction(screenId, action, xrandrOut)\n\tlocal xrandrOpts = ''\n    if screenId then\n        local configuration = screens[screenId]\n        if configuration then\n            if configuration[action] then -- get xrandr options\n                xrandrOpts = configuration[action](xrandrOut)\n            end\n        else -- configuration not found, append configuration template\n            if tostring(screenId):len() ~= 0 and not hasConfigurationFor(screenId) then\n                naughty.notify({text = 'Append new configuration for screen id: ' .. screenId})\n                appendConfiguration(screenId, xrandrOut)\n            end\n        end\n    end\n\n    if xrandrOpts:len() == 0 then -- use default configuration if specific was not found\n        xrandrOpts = screens['default'][action](xrandrOut)\n    end\n    if xrandrOpts then\n        setupScreen(xrandrOpts)\n    end\nend\n\nlocal function disableOutput(out, changedCard)\n\tlocal xrandrOut = getXrandrOutput(out, changedCard)\n\tlocal screenId = getScreenId(out)\n    performConfiguredAction(screenId, 'disconnected', xrandrOut)\n    naughty.notify({ text='Output ' .. xrandrOut .. ' disconnected' })\nend\n\nlocal function enableOutput(out, changedCard)\n\tlocal xrandrOut = getXrandrOutput(out, changedCard)\n\tlocal screenId = getScreenId(out)\n    performConfiguredAction(screenId, 'connected', xrandrOut)\nend\n\nlocal cardDev = dev .. card\nlocal outputs = connectedOutputs(cardDev, card)\n\nfunction updateScreens(changedCard)\n\tlocal newCardDev = dev .. changedCard\n\tlocal newOutputs = connectedOutputs(newCardDev, changedCard)\n\tlocal mergedOutputs = mergeTables(outputs, newOutputs)\n\n\tfor out in pairs(mergedOutputs) do\n\t\tif not outputs[out] then -- connected\n\t\t\tenableOutput(out, changedCard)\n\t\telseif not newOutputs[out] then -- disconnected\n\t\t\tdisableOutput(out, changedCard)\n\t\tend\n\tend\n\toutputs = newOutputs\n\n    -- reinit awesome\n    awesome.restart()\nend\n\n"
  },
  {
    "path": "screens_db.lua",
    "content": "local naughty = require(\"naughty\")\n\nlocal defaultOutput = 'eDP1'\n\noutputMapping = {\n    ['DP-1'] = 'DP1',\n    ['DP-2'] = 'DP2',\n    ['DP-3'] = 'DP3',\n    ['VGA-1'] = 'VGA1',\n    ['LVDS-1'] = 'LVDS1',\n    ['HDMI-A-1'] = 'HDMI1',\n    ['HDMI-A-2'] = 'HDMI2',\n    ['eDP-1'] = 'eDP1',\n    ['eDP-2'] = 'eDP2',\n}\n\nscreens = {\n\t['default'] = {\n\t\t['connected'] = function (xrandrOutput)\n            if xrandrOutput ~= defaultOutput then\n                return '--output ' .. xrandrOutput .. ' --auto --same-as ' .. defaultOutput\n            end\n            return nil\n\t\tend,\n\t\t['disconnected'] = function (xrandrOutput)\n            if xrandrOutput ~= defaultOutput then\n                return '--output ' .. xrandrOutput .. ' --off --output ' .. defaultOutput .. ' --auto'\n            end\n            return nil\n\t\tend\n\t},\n}\n"
  }
]