Только что прошел еще один очень важный этап в своем проекте USBTerm.
Поскольку я собираюсь отображать на своем графическом терминале экран виртуальной машины VMWare, то мне принципиально важно понять, как я это буду делать. И вообще, возможно ли это в принципе.
Я собираюсь копировать изображение декстопа виртуальной машины на USB терминал, но как сделать это с минимальными издержками, с максимальной производительностью? Понятно, что нужно переносить по шине USB только изменения на экране, ведь изменений, как правило, не очень много. Если удастся копировать только изменившиеся участки экрана, то производительность обещает быть приемлимой.
Поскольку мы высококвалифицированные программисты, то нам удалось нагуглить некоторое решение. :-)
Цель проекта USBTerm - создание недорогого терминала, тонкого клиента, на базе платы Марсоход3bis для работы в среде Windows 8.1 или Windows 10. Выбор Windows - как самой распространенной ОС. Поэтому, я веду поиск решения именно для Windows.
Вот.. Для Windows когда-то майкрософт предлагала писать дисплейный драйвер mirror - зеркало. Этот дисплейный драйвер mirror принимал копии всех вызовов к основному дисплейному драйверу. Сейчас, для Windows 8 и выше, этот метод похоже уже не работает.
Взамен драйверам mirror, компания microsoft теперь предлагает пользоваться специальным Desktop Duplication API https://msdn.microsoft.com/en-us/library/windows/desktop/hh404487(v=vs.85).aspx(v=vs.85).aspx
Это API интенсивно использует DirectX и позволяет:
- получить текущее изображение с экрана компьютера функцией IDXGIOutputDuplication::AcquireNextFrame;
- получить координаты прямоугольных областей экрана, которые были изменены, IDXGIOutputDuplication::GetFrameDirtyRects;
- получить координаты прямоугольных областей экрана, которые были перемещены, IDXGIOutputDuplication::GetFrameMoveRects;
- получить изображение и местоположение указателя мыши функцией IDXGIOutputDuplication::GetFramePointerShape.
К сожалению, я не являюсь знатоком DIrectX, так что пришлось изрядно потрудиться, чтобы все это понять, переосмыслить и применить к моему терминалу. Мне требовалось доказать хотя бы самом себе, что проект идет в правильном направлении.
Microsoft предлагает исходный код проекта, который демонстрирует возможности DesktopDuplication API вот здесь: можно просто взять и пробовать https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a Однако, мне этот пример не совсем подходит. Точнее, совсем не подходит, так как он хватает содержимое дисплея, но отображает его в windows окне программы. В этом примере получается как-то вот так:
Мне вообще не нужно никакого моего окна на экране. И мне нужно получить попиксельный доступ изменившимся областям иэображения экрана. Из-за этого мне пришлось взять исходный пример майкрософта и значительно его переработать.
В DirectX, как я понял, изображения размещаются в текстурах ID3D11Texture2D. И размещаться они, эти текстуры, могут либо в памяти видеоадаптера (графического ускорителя), либо в системной памяти. Если в системной памяти - тогда можно получить доступ процессора к памяти текстуры, если сделать ID3D11DeviceContext.Map(...).
Вот и приходится получить новый фрейм и список прямоугольных участков, которые были изменены на экране, потом копировать текстуру из фрейма в мою текстуру, находящуюся в системной памяти, делать Map() и получать доступ к пикселам. Уж потом формировать блоки команд для USBTerm и посылать фрагменты изображения не терминал.
Вот сделал простейшую демонстрацию этой всей системы.
Исходники, конечно, на GITHUB: https://github.com/marsohod4you/UsbHwThinClient4Vm/tree/master/cpp/test
Плата Марсоход3bis с зашитым в ПЛИС проектом терминала подключается к ноутбуку. На ноутбуке запускаю VMWare Player, который запускает виртуальную машину с ОС Windows 8.1. В виртуальную машину пробрасываю подключенное устройство USB FTDI с платы Марсоход3bis.
Теперь в виртуальной машине запускаю свою тестовую программу test.exe, которая копирует экран виртуальной машина в плату.
Виртуальную машину можно свернуть в иконку и делать на ноутбуке какую-то свою работу, на терминале - свой экран, своя виндовс и свое рабочее место. На видео демонстрации в начале этой статьи все это видно.
Ну пока это только предварительные тесты. Тестовая программа очень далека от совершенства. Ее нужно оформить в виде сервиса Windows, она должна уметь переключаться между десктопами (в виндовс как минимум всегда два десктопа: первый, где выполняется логин пользователя, второй - рабочий десктоп), нужно, чтобы программа не ломалась, когда меняется разрешение на экране виртуальной машины, ну и с производительностью еще нужно поработать, я сейчас пользуюсь функцией ID3D11DeviceContext::CopyResource(..) для копирования текстур, а нужно использовать ID3D11DeviceContext::CopyResourceRegion(..)
В общем работа еще много. Ну и теперь, когда я понял, что с виртуальной машиной задумка верная, займусь подключением USB мыши и USB клавиатуры к плате Marsohod3bis.
Подробнее...