You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

monitordisplay 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #!/usr/bin/php
  2. <?php
  3. function error($message)
  4. {
  5. file_put_contents('php://stderr', $message."\n");
  6. }
  7. function notice($message)
  8. {
  9. file_put_contents('php://stdin', $message."\n");
  10. }
  11. function filter(array $data, array $default)
  12. {
  13. foreach ($data as $k => $v) {
  14. if (!array_key_exists($k, $default)) {
  15. unset($data[$k]);
  16. } elseif (is_array($default[$k]) && !is_array($v)) {
  17. $data[$k] = (array) $v;
  18. }
  19. }
  20. foreach ($default as $k => $v) {
  21. if (!array_key_exists($k, $data)) {
  22. $data[$k] = $v;
  23. }
  24. }
  25. return $data;
  26. }
  27. function filterScreen(array $data)
  28. {
  29. $default = [
  30. 'name' => null,
  31. 'resolutionX' => null,
  32. 'resolutionY' => null,
  33. ];
  34. return filter($data, $default);
  35. }
  36. function filterMode(array $data)
  37. {
  38. $default = [
  39. 'config' => [],
  40. 'primary' => null,
  41. 'before_command' => null,
  42. 'after_command' => null,
  43. ];
  44. $data = filter($data, $default);
  45. foreach ($data['config'] as $k => $v) {
  46. $v = explode(',', $v);
  47. $v = array_map('trim', $v);
  48. $data['config'][$k] = $v;
  49. }
  50. return $data;
  51. }
  52. function getCurrentModeFile()
  53. {
  54. return sys_get_temp_dir().'/monitordisplay.tmp';
  55. }
  56. function getCurrentMode()
  57. {
  58. $currentModeFile = getCurrentModeFile();
  59. if (file_exists($currentModeFile)) {
  60. $currentMode = (array) @parse_ini_file($currentModeFile);
  61. } else {
  62. $currentMode = [];
  63. }
  64. return filter($currentMode, [
  65. 'mode' => null,
  66. 'config' => null,
  67. ]);
  68. }
  69. function build(array $modes, array $screens, $mode, $toggle, array $currentMode, $addOff)
  70. {
  71. if ($mode === null) {
  72. if (!array_key_exists($currentMode['mode'], $modes)) {
  73. return;
  74. }
  75. $tmpMode = $currentMode['mode'];
  76. $mode = $modes[$currentMode['mode']];
  77. } else {
  78. if (!array_key_exists($mode, $modes)) {
  79. return;
  80. }
  81. $tmpMode = $mode;
  82. $mode = $modes[$mode];
  83. }
  84. if ($toggle !== false) {
  85. if ($currentMode['mode'] === null) {
  86. return;
  87. }
  88. if ($currentMode['config'] === null) {
  89. return;
  90. }
  91. $configs = $mode['config'];
  92. $configsKeys = array_keys($configs);
  93. if (!array_key_exists($currentMode['config'], $mode['config'])) {
  94. $currentMode['config'] = $configsKeys[0];
  95. }
  96. if ($toggle === true) {
  97. foreach ($configsKeys as $k => $key) {
  98. if ($key == $currentMode['config']) {
  99. $currentKey = $k;
  100. }
  101. }
  102. ;
  103. $config = $configsKeys[($currentKey + 1) % count($configsKeys)];
  104. } elseif (array_key_exists($toggle, $configs)) {
  105. $config = $toggle;
  106. } else {
  107. error(sprintf('Invalid config "%s"', $toggle));
  108. die(1);
  109. }
  110. } else {
  111. $config = array_keys($mode['config'])[0];
  112. }
  113. if (!array_key_exists($config, $mode['config'])) {
  114. return;
  115. }
  116. $tmpConfig = $config;
  117. $config = $mode['config'][$config];
  118. $command = ['xrandr'];
  119. $x = 0;
  120. $screensUsed = [];
  121. foreach ($config as $screen) {
  122. if (!array_key_exists($screen, $screens)) {
  123. return;
  124. }
  125. $tScreen = $screens[$screen];
  126. $name = $tScreen['name'];
  127. $resolutionX = $tScreen['resolutionX'];
  128. $resolutionY = $tScreen['resolutionY'];
  129. if (empty($name) || empty($resolutionX) || empty($resolutionY)) {
  130. error(sprintf('Invalid screen configuration "%s"', $screen));
  131. die(1);
  132. }
  133. $command[] = '--output';
  134. $command[] = escapeshellarg($name);
  135. $screensUsed[] = $name;
  136. if ($mode['primary'] === $name) {
  137. $command[] = '--primary';
  138. }
  139. $command[] = '--mode';
  140. $command[] = sprintf('%dx%d', $resolutionX, $resolutionY);
  141. $command[] = '--pos';
  142. $command[] = sprintf('%dx%d', $x, 0);
  143. $x += (int) $resolutionX;
  144. }
  145. if ($addOff) {
  146. foreach ($screens as $screen) {
  147. if (!in_array($screen['name'], $screensUsed)) {
  148. $command[] = '--output';
  149. $command[] = escapeshellarg($screen['name']);
  150. $command[] = '--off';
  151. }
  152. }
  153. }
  154. $command = implode(' ', $command);
  155. return [
  156. 'config' => sprintf("mode=%s\nconfig=%s", $tmpMode, $tmpConfig),
  157. 'command' => $command,
  158. 'mode' => $mode,
  159. ];
  160. }
  161. $files = [
  162. '/etc/monitordisplay/config.ini',
  163. getenv('HOME').'/.config/monitordisplay/config.ini',
  164. getenv('HOME').'/.monitordisplay',
  165. ];
  166. $config = [];
  167. $currentMode = getCurrentMode();
  168. foreach ($files as $file) {
  169. if (file_exists($file)) {
  170. $parsing = @parse_ini_file($file, true);
  171. if ($parsing === false) {
  172. error(sprintf('The file "%s" is not a valid ini file', $file));
  173. continue;
  174. }
  175. notice(sprintf('Configuration file merged: %s', $file));
  176. $config = array_merge(
  177. $config,
  178. $parsing
  179. );
  180. }
  181. }
  182. if (empty($config)) {
  183. error('No configuration found');
  184. error(sprintf('Paths checked: \n%s', implode(', ', $files)));
  185. die(1);
  186. }
  187. $screens = [];
  188. $modes = [];
  189. foreach ($config as $section => $values) {
  190. $type = explode(':', $section);
  191. if (count($type) !== 2) {
  192. error(sprintf('Invalid section "%s". Ignored', $section));
  193. }
  194. if ($type[0] === 'screen') {
  195. $screens[$type[1]] = filterScreen($values);
  196. } elseif ($type[0] === 'mode') {
  197. $modes[$type[1]] = filterMode($values);
  198. } else {
  199. error(sprintf('Section "%s" ignored', $section));
  200. }
  201. }
  202. if (empty($screens)) {
  203. error('No screen found');
  204. die(1);
  205. }
  206. if (empty($modes)) {
  207. error('No mode found');
  208. die(1);
  209. }
  210. $options = getopt('m:t::s');
  211. $mode = isset($options['m']) ? $options['m'] : null;
  212. $toggle = isset($options['t']) ? (false === $options['t'] ? true : $options['t']) : false;
  213. $addOff = !isset($options['s']);
  214. $build = build($modes, $screens, $mode, $toggle, $currentMode, $addOff);
  215. if (!empty($build)) {
  216. file_put_contents(getCurrentModeFile(), $build['config']);
  217. $commands = [
  218. $build['mode']['before_command'],
  219. $build['command'],
  220. $build['mode']['after_command']
  221. ];
  222. $output = [];
  223. foreach ($commands as $command) {
  224. if (!empty($command)) {
  225. $output[] = 'Command: '.$command;
  226. exec($command, $output);
  227. }
  228. }
  229. notice(implode("\n", $output));
  230. die(0);
  231. }
  232. error(sprintf('Invalid mode "%s"', $mode));
  233. die(1);