#!/usr/bin/php $v) { if (!array_key_exists($k, $default)) { unset($data[$k]); } elseif (is_array($default[$k]) && !is_array($v)) { $data[$k] = (array) $v; } } foreach ($default as $k => $v) { if (!array_key_exists($k, $data)) { $data[$k] = $v; } } return $data; } function filterScreen(array $data) { $default = [ 'name' => null, 'resolutionX' => null, 'resolutionY' => null, 'dpi' => null, 'xrandr_extra' => null, ]; return filter($data, $default); } function filterMode(array $data) { $default = [ 'config' => [], 'primary' => null, 'before_command' => null, 'after_command' => null, ]; $data = filter($data, $default); foreach ($data['config'] as $k => $v) { $v = explode(',', $v); $v = array_map('trim', $v); $data['config'][$k] = $v; } return $data; } function getCurrentModeFile() { return sys_get_temp_dir().'/monitordisplay.tmp'; } function getCurrentMode() { $currentModeFile = getCurrentModeFile(); if (file_exists($currentModeFile)) { $currentMode = (array) @parse_ini_file($currentModeFile); } else { $currentMode = []; } return filter($currentMode, [ 'mode' => null, 'config' => null, ]); } function build(array $modes, array $screens, $mode, $toggle, array $currentMode, $addOff) { if (null === $mode) { if (!array_key_exists($currentMode['mode'], $modes)) { return; } $tmpMode = $currentMode['mode']; $mode = $modes[$currentMode['mode']]; } else { if (!array_key_exists($mode, $modes)) { return; } $tmpMode = $mode; $mode = $modes[$mode]; } if (false !== $toggle) { $configs = $mode['config']; $configsKeys = array_keys($configs); if (null === $currentMode['mode']) { $currentMode['config'] = $configsKeys[0]; } if (!array_key_exists($currentMode['config'], $mode['config'])) { $currentMode['config'] = $configsKeys[0]; } if (true === $toggle) { foreach ($configsKeys as $k => $key) { if ($key == $currentMode['config']) { $currentKey = $k; } } $config = $configsKeys[($currentKey + 1) % count($configsKeys)]; } elseif (array_key_exists($toggle, $configs)) { $config = $toggle; } else { error(sprintf('Invalid config "%s"', $toggle)); die(1); } } else { $config = array_keys($mode['config'])[0]; } if (!array_key_exists($config, $mode['config'])) { return; } $tmpConfig = $config; $config = $mode['config'][$config]; $command = ['xrandr']; $x = 0; $screensUsed = []; foreach ($config as $screen) { if (!array_key_exists($screen, $screens)) { return; } $tScreen = $screens[$screen]; $name = $tScreen['name']; $resolutionX = $tScreen['resolutionX']; $resolutionY = $tScreen['resolutionY']; $dpi = $tScreen['dpi']; $xrandrExtra = $tScreen['xrandr_extra']; if (empty($name) || empty($resolutionX) || empty($resolutionY)) { error(sprintf('Invalid screen configuration "%s"', $screen)); die(1); } $command[] = '--output'; $command[] = escapeshellarg($name); $screensUsed[] = $name; if ($mode['primary'] === $name) { $command[] = '--primary'; } if (null !== $dpi) { $command[] = sprintf('--dpi %d', $dpi); } $command[] = '--mode'; $command[] = sprintf('%dx%d', $resolutionX, $resolutionY); $command[] = '--pos'; $command[] = sprintf('%dx%d', $x, 0); $x += (int) $resolutionX; if (null !== $xrandrExtra) { $command[] = $xrandrExtra; } } if ($addOff) { foreach ($screens as $screen) { if (!in_array($screen['name'], $screensUsed)) { $command[] = '--output'; $command[] = escapeshellarg($screen['name']); $command[] = '--off'; } } } $command = implode(' ', $command); return [ 'config' => sprintf("mode=%s\nconfig=%s", $tmpMode, $tmpConfig), 'command' => $command, 'mode' => $mode, ]; } $files = [ '/etc/monitordisplay/config.ini', getenv('HOME').'/.config/monitordisplay/config.ini', getenv('HOME').'/.monitordisplay', ]; $config = []; $currentMode = getCurrentMode(); foreach ($files as $file) { if (file_exists($file)) { $parsing = @parse_ini_file($file, true); if (false === $parsing) { error(sprintf('The file "%s" is not a valid ini file', $file)); continue; } notice(sprintf('Configuration file merged: %s', $file)); $config = array_merge( $config, $parsing ); } } if (empty($config)) { error('No configuration found'); error(sprintf('Paths checked: \n%s', implode(', ', $files))); die(1); } $screens = []; $modes = []; foreach ($config as $section => $values) { $type = explode(':', $section); if (2 !== count($type)) { error(sprintf('Invalid section "%s". Ignored', $section)); } if ('screen' === $type[0]) { $screens[$type[1]] = filterScreen($values); } elseif ('mode' === $type[0]) { $modes[$type[1]] = filterMode($values); } else { error(sprintf('Section "%s" ignored', $section)); } } if (empty($screens)) { error('No screen found'); die(1); } if (empty($modes)) { error('No mode found'); die(1); } $options = getopt('m:t::sn'); $mode = isset($options['m']) ? $options['m'] : null; $toggle = isset($options['t']) ? (false === $options['t'] ? true : $options['t']) : false; $addOff = !isset($options['s']); $isTest = isset($options['n']); $build = build($modes, $screens, $mode, $toggle, $currentMode, $addOff); if (!empty($build)) { file_put_contents(getCurrentModeFile(), $build['config']); $commands = [ $build['mode']['before_command'], $build['command'], $build['mode']['after_command'], ]; $output = []; if ($isTest) { $output[] = 'Mode TEST'; } foreach ($commands as $command) { if (!empty($command)) { $output[] = 'Command: '.$command; if (!$isTest) { exec($command, $output); } } } notice(implode("\n", $output)); die(0); } error(sprintf('Invalid mode "%s"', $mode)); die(1);