From 84d6379392b9f580424ef1ef25c499e698a7d69f Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Fri, 23 Apr 2021 16:02:00 +0200 Subject: pkgs/profpatsch: add xrandr two monitor setup Just to prove I can. --- pkgs/profpatsch/xrandr.nix | 194 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 pkgs/profpatsch/xrandr.nix (limited to 'pkgs/profpatsch/xrandr.nix') diff --git a/pkgs/profpatsch/xrandr.nix b/pkgs/profpatsch/xrandr.nix new file mode 100644 index 00000000..fd8c66d0 --- /dev/null +++ b/pkgs/profpatsch/xrandr.nix @@ -0,0 +1,194 @@ +{ pkgs, getBins, writeExecline, runExeclineLocal, toNetstringKeyVal, dhall-json, ... }: + +let + inherit (pkgs) lib; + bins = getBins pkgs.nodejs [ "node" ] + // getBins pkgs.coreutils [ "echo" "ln" "mkdir" ] + // getBins dhall-json [ "json-to-dhall" ] + // getBins pkgs.xorg.xrandr [ "xrandr" ] + ; + + writeNodejs = { + name, + # an attrset of node dependency name to source directory; + # will be made into a node_modules directory and set as `NODE_PATH`. + dependencies + }: + let + node_modules = runExeclineLocal "${name}-node_modules" { + stdin = toNetstringKeyVal dependencies; + } [ + "importas" "out" "out" + "if" [ bins.mkdir "$out" ] + "forstdin" "-o" "0" "-Ed" "" "dep" + "multidefine" "-d" "" "$dep" [ "name" "source" ] + "if" [ bins.echo "\${name} - \${source}" ] + bins.ln "-sT" "\${source}" "\${out}/\${name}" + ]; + + in pkgs.writers.makeScriptWriter { + interpreter = writeExecline "nodejs-with-modules" {} [ + "export" "NODE_PATH" node_modules + bins.node "$@" + ]; + } name; + + dhall-typecheck = { + name, + dhallType, + recordsLoose ? false + }: + writeExecline name {} ([ + bins.json-to-dhall + ] + ++ lib.optional recordsLoose "--records-loose" + ++ [ + dhallType + ]); + + parse = writeNodejs { + name = "xrandr-parse"; + dependencies = { + xrandr-parse = + (pkgs.fetchFromGitHub { + owner = "lionep"; + repo = "xrandr-parse"; + rev = "a35bfd625c1b0834aa94f136cf8282b25624b3e6"; + sha256 = "0c8mfsvgg76ia2i9gsgdwy567xzba5274xpj8si37yah5qpp8dkm"; + }); + }; + } '' + var parse = require('xrandr-parse'); + var exec = require('child_process').exec; + + exec('xrandr', function (err, stdout) { + var query = parse(stdout); + console.log(JSON.stringify(query, null, 2)); + }); + ''; + + type = pkgs.writeText "type.dhall" '' + let Resolution = { + height: Text, + width: Text, + rate: Double + } + let Monitor = + < NotConnected : {} + | Connected : { + connected: Bool, + modes: List Resolution, + index: Natural, + native: Resolution + } + | Active : { + modes: List Resolution, + index: Natural, + native: Resolution, + current: Resolution + } > + + in List { + mapKey: Text, + mapValue: Monitor + } + ''; + + + two-monitor-setup = writeExecline "test" {} [ + "backtick" "-Ei" "json" [ parse ] + "if" [ + "pipeline" [ bins.echo "$json" ] + "redirfd" "-w" "1" "/dev/null" + (dhall-typecheck { + name = "typecheck-xrandr"; + dhallType = type; + recordsLoose = true; + }) + ] + "pipeline" [ bins.echo "$json" ] + two-monitor-setup-script + ]; + + + two-monitor-setup-script = pkgs.writers.writePython3 "xrandr-two-monitor-setup" {} '' + import json + import sys + import os + + # TODO: use netencode for input + monitors = json.load(sys.stdin) + + connected = { + k: v for k, v in monitors.items() + if 'connected' in v and v['connected'] + } + + if 'eDP1' not in connected: + print("could not find eDP1 (laptop screen)", file=sys.stderr) + sys.exit(1) + + if len(connected) != 2: + print("only know how to configure two monitors") + + eDP1 = connected['eDP1'] + + # laptop screen is active + assert('current' in eDP1) + + # how far the laptop screen should be offset to end + # at the bottom of the monitor + h_offset = 0 + for k, v in connected.items(): + if k == 'eDP1': + assert('current' in v) + + else: + h = int(v['native']['height']) + h_offset = h - int(eDP1['native']['height']) + external_monitor = (k, v) + # can’t handle bigger laptop screens atm + assert(h_offset >= 0) + + xrandr_command = [ + "--verbose", + + "--output", external_monitor[0], + "--primary", + "--mode", "{}x{}".format( + external_monitor[1]['native']['width'], + external_monitor[1]['native']['height'] + ), + "--pos", "{}x{}".format( + # offset by the laptop size to the right + eDP1['native']['width'], + # monitor starts at 0 + 0 + ), + + "--output", "eDP1", + "--mode", "{}x{}".format( + eDP1['native']['width'], + eDP1['native']['height'], + ), + "--pos", "{}x{}".format( + # laptop is the leftmost screen + 0, + # but offset to the bottom so that it always aligns on the left bottom + h_offset + ), + ] + + os.execvp( + "${bins.xrandr}", + ["${bins.xrandr}"] + + xrandr_command + ) + ''; + +in { + inherit + parse + two-monitor-setup + ; +} -- cgit 1.4.1