Tool for laying out displays on Linux
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.
 
 

181 lines
4.3 KiB

<?php
declare(strict_types=1);
namespace App;
/**
* Calculate the layout of a collection of screens.
*
* @author Adam Pippin <hello@adampippin.ca>
*/
class Layout
{
/**
* List of screens this layout uses.
* @var array<string,array>
*/
protected $_screens;
/**
* Relations between the screens.
* @var array[]
*/
protected $_links;
/**
* Initialize a new layout.
*
* @param array[] $screens
* @param array[] $links
*/
public function __construct(array $screens, array $links)
{
$this->_screens = $screens;
$this->_links = $links;
}
/**
* Fetch the names of the screens this layout depends on.
*
* @return string[]
*/
public function getScreenNames(): array
{
return array_keys($this->_screens);
}
/**
* Execute this layout.
*
* @param Configuration $config
* @param ILayoutDriver $driver
* @return void
*/
public function execute(Configuration $config, ILayoutDriver $driver): void
{
$primary_name = $config->getPrimaryScreenName();
$primary = $config->getPrimaryScreen();
$layout_stack = [$primary_name];
$did_layout = false;
$layout = [$primary_name => ['x' => 0, 'y' => 0]];
foreach ($this->_screens as $screen_name => $screen_data)
{
if (isset($screen_data['width']) && isset($screen_data['height']))
{
$config->getScreen($screen_name)->setDimensions($screen_data['width'], $screen_data['height']);
}
}
while (sizeof($layout_stack))
{
$did_layout = false;
$match_screen = array_pop($layout_stack);
foreach ($this->_links as $link)
{
$target_name = $link['display'];
$source_name = $link['above'] ?? $link['below'] ?? $link['right_of'] ?? $link['left_of'];
if ($source_name == $match_screen)
{
$did_layout = true;
$calculate_layout = null;
if (isset($link['above']))
{
/**
* @return int[]
*/
$calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array {
$center = $s_x + ($s_w / 2);
$x = $center - ($d_w / 2);
$y = $s_y - $d_h;
return [$x, $y];
};
}
elseif (isset($link['below']))
{
/**
* @return int[]
*/
$calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array {
$center = $s_x + ($s_w / 2);
$x = $center - ($d_w / 2);
$y = $s_y + $s_h;
return [$x, $y];
};
}
elseif (isset($link['left_of']))
{
/**
* @return int[]
*/
$calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array {
return [$s_x - $d_w, $s_y];
};
}
elseif (isset($link['right_of']))
{
/**
* @return int[]
*/
$calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array {
return [$s_x + $s_w, $s_y];
};
}
$source = $config->getScreen($source_name);
$target = $config->getScreen($target_name);
if (isset($layout[$source_name]))
{
$s_x = $layout[$source_name]['x'];
$s_y = $layout[$source_name]['y'];
}
else
{
[$s_x, $s_y] = $source->getOffset();
}
[$s_w, $s_h] = $source->getDimensions();
if (isset($layout[$target_name]))
{
$d_x = $layout[$target_name]['x'];
$d_y = $layout[$target_name]['y'];
}
else
{
[$d_x, $d_y] = $target->getOffset();
}
[$d_w, $d_h] = $target->getDimensions();
[$x, $y] = $calculate_layout($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h);
$layout[$target_name] = ['x' => $x, 'y' => $y];
$layout_stack[] = $target_name;
}
}
}
$min_x = PHP_INT_MAX;
$min_y = PHP_INT_MAX;
foreach ($layout as $screen_name => $screen_offset)
{
$min_x = min($screen_offset['x'], $min_x);
$min_y = min($screen_offset['y'], $min_y);
}
foreach ($layout as $screen_name => $screen_offset)
{
[$current_x, $current_y] = $config->getScreen($screen_name)->getOffset();
if ($current_x != $screen_offset['x'] - $min_x || $current_y != $screen_offset['y'] - $min_y)
{
$config->getScreen($screen_name)->setOffset($screen_offset['x'] - $min_x, $screen_offset['y'] - $min_y);
}
}
}
}