DRM: рисовать в видеобуфер без Xorg

Модератор: Модераторы разделов

Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

Привет всем!
Собственно как создать видеобуфер и рисовать там все что угодно я уже раскопал:
1. Открыть "файл" видеокарты (fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC")
2. Затем несколько libdrm функций типа:
- drmModeGetResources(fd);
- drmModeGetConnector(fd, res->connectors[i]);
- drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
- drmModeAddFB(fd, dev->width, dev->height, 24, 32, dev->stride, dev->handle, &dev->fb);
- drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
- dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset);
3. В результате чего все что будет записано в dev->map тут же отобразиться на экране.

Все здорово, можно рисовать, но я уперся в проблему с мышью, как ее отобразить. В libdrm есть только три функции для работы с мышью:
- int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height);
- int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y);
- int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)

Как я не бился и не гуглил ничего толкового не нашел, копался в сырцах weston, но там несколько десятков тысяч строк кода и все что удалось нарыть:
Код
/* Turn off hardware cursor */
drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);

handle = gbm_bo_get_handle(bo).s32;
if (drmModeSetCursor(c->drm.fd, output->crtc_id, handle, 64, 64))
{
weston_log("failed to set cursor: %m\n");
}


Как я понял gbm_bo_get_handle(bo).s32; это что то из GL, а без GL никак что ли.

Нет можно конечно еще читать напрямую из /dev/input/event4 (мышь у меня именно тут) или /dev/input/mouse0, но для доступа даже для чтения нужны права рута, а как от простого юзера хотя бы получить координаты мышки и какой кнопкой щелкнули, уж на крайняк сам курсор то можно нарисовать самому из PNG. Интересует только настоящий код, т.е. не GTK/QT и даже не Xlib, чистый С + libdrm + libkms + libpixman + системные функции типа ioctl(fd, NULL);
Может кто знает :)
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение eddy »

Есть же демон для поддержки мыши в консоли: gpm. Смотри ее исходники. Она-то без иксов работает!
Кстати, а зачем прямо вот так - напрямую? Неужто хотя бы фреймбуфер готовый нельзя использовать?
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

eddy писал(а):
18.06.2014 09:04
Есть же демон для поддержки мыши в консоли: gpm. Смотри ее исходники. Она-то без иксов работает!
Кстати, а зачем прямо вот так - напрямую? Неужто хотя бы фреймбуфер готовый нельзя использовать?


Про gpm не знал, посмотрю, только будет ли она полезна в KMS/DRM, это ж не чистая консоль (текстовый режим), а уже графический, 32 битный режим с поддержкой 2D и 3D и всеми наворотами.
Фреймбуфер не хочу т.к. это устаревшая мулька, я не спец, но читал в интернетах, что framebuffer поддерживает только 2D, вроде можно и 3D, с GL-ем, но все равно уже старье, не говоря уже про Xorg, которому давно пора на покой.
Не давно узнал про Wayland/Weston, пытался разобраться как это работает, но там такого наворочено, что мне не разобраться. Т.к. я программлю для удовольствия, а не для денег, то мой уровень довольно низкий, вот я и пытаюсь повысить его, опять же для удовольствия, что бы мозги не засохли.
Ну и лучший способ кодить - это кодить живой код. До этого пытался кодить под Xlib, но уж больно заковыристое API у иксов, а тут как раз про wayland узнал, кинулся было, но проект еще сырой и на стадии разработки, да и в чужом коде копаться еще та радость. Наткнулся в сети на пример с DRM и оказалось, что и Xlib и тот же Wayland юзают KMS/DRM, т.е. они тупо обертки, как GTK/QT обертки над настоящим С/С++, а зачем нам обертки, это не наш метод.
В общем хочу для себя покодить, оконный + композитный менеджер на чистом DRM+pixman, интересно просто, да и для мозгов полезно, а готовое я и так юзаю конечно.
Рисовать окна с прозрачностью, грузить jpeg+png уже могу, но без мышки GUI не тот, Не пойму зачем drmModeSetCursor нужна, она тупо рисует заранее загруженный курсор как картинку на экране или она именно устанавливает. Название то говорит set cursor, а не draw cursor, но что то никак не могу поднять мышку drm-ом, может он просто этого не умеет, а я ломлюсь в закрытые двери :)

Нарыл только что:

* Where does libevdev sit?
* ========================
*
* libevdev is essentially a `read(2)` on steroids for `/dev/input/eventX
* devices. It sits below the process that handles input events, in between
* the kernel and that process. In the simplest case, e.g. an evtest-like tool
* the stack would look like this:
*
* kernel → libevdev → evtest
*
* For X.Org input modules, the stack would look like this:
*
* kernel → libevdev → xf86-input-evdev → X server → X client
*
* For Weston/Wayland, the stack would look like this:
*
* kernel → libevdev → Weston → Wayland client
*
* libevdev does **not** have knowledge of X clients or Wayland clients, it is
* too low in the stack.

Оказывается и Xorg и Wayland юзают libevdev или я не правильно перевел (would look - это типа могло БЫ выглядеть как), а не работают напрямую с /dev/input/event*, в сорцах Weston вроде как юзается udev, который наверняка обертка над libevdev, а может и нет.
К сожалению libevdev тоже требует прав рута:

Код: Выделить всё

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>

#include <libevdev-1.0/libevdev/libevdev.h>

int main()
{
    struct libevdev *dev = NULL;
    int fd;
    int rc = 1;

    fd = open("/dev/input/event4", O_RDONLY|O_NONBLOCK);
    if(fd < 0)
    {
        fprintf(stderr, "cannot open fd: %m\n");
        exit(1);
    }

    rc = libevdev_new_from_fd(fd, &dev);
    if(rc < 0)
    {
        fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
        exit(1);
    }
    printf("Input device name: \"%s\"\n", libevdev_get_name(dev));
    printf("Input device ID: bus %#x vendor %#x product %#x\n",

    libevdev_get_id_bustype(dev),
    libevdev_get_id_vendor(dev),
    libevdev_get_id_product(dev));
    if (!libevdev_has_event_type(dev, EV_REL) || !libevdev_has_event_code(dev, EV_KEY, BTN_LEFT))
    {
        printf("This device does not look like a mouse\n");
        exit(1);
    }

    do {
        struct input_event ev;
        rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
        if(rc == 0) printf("Event: %s %s %d\n",
        libevdev_get_event_type_name(ev.type),
        libevdev_get_event_code_name(ev.type, ev.code), ev.value);
    } while (rc == 1 || rc == 0 || rc == -EAGAIN);

    return 0;
}


Все классно работает, но только от рута. Так что выходит не получиться от простого юзера попользовать низкоуровневые функции, либо разделять на сервер с правами рута и клиент-юзер, либо давать права рута бинарнику.
Хотя как то странно получается, я как юзер могу получить доступ к /dev/dri/card0, т.е. монитору, а к мыши и клаве не могу, не логично мне думается, но опять же я не спец, девелоперам виднее :)
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

sabir писал(а):
18.06.2014 10:58
eddy писал(а):
18.06.2014 09:04
Есть же демон для поддержки мыши в консоли: gpm. Смотри ее исходники. Она-то без иксов работает!
Кстати, а зачем прямо вот так - напрямую? Неужто хотя бы фреймбуфер готовый нельзя использовать?


Нарыл только что:

* Where does libevdev sit?
* ========================
*
* libevdev is essentially a `read(2)` on steroids for `/dev/input/eventX
* devices. It sits below the process that handles input events, in between
* the kernel and that process. In the simplest case, e.g. an evtest-like tool
* the stack would look like this:
*
* kernel → libevdev → evtest
*
* For X.Org input modules, the stack would look like this:
*
* kernel → libevdev → xf86-input-evdev → X server → X client
*
* For Weston/Wayland, the stack would look like this:
*
* kernel → libevdev → Weston → Wayland client
*
* libevdev does **not** have knowledge of X clients or Wayland clients, it is
* too low in the stack.


Как оказалось libevdev это тоже обертка над read() и т.д., можно напрямую читать из /dev/input/event*, Что то вроде:

Код: Выделить всё

char buffer[3];
read(fd, buffer, sizeof(buffer));

Ради прикола посмотрел сорцы libdrm и уже без удивления обнаружил, что это тоже обертка, например:

Код: Выделить всё

int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,
                 uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
         uint32_t *buf_id)
{
    struct drm_mode_fb_cmd f;
    int ret;

    f.width  = width;
    f.height = height;
    f.pitch  = pitch;
    f.bpp    = bpp;
    f.depth  = depth;
    f.handle = bo_handle;

    if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB, &f)))
        return ret;

    *buf_id = f.fb_id;
    return 0;
}


в итоге вызывает

Код: Выделить всё

if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB, &f)))


который есть ни что иное как

Код: Выделить всё

static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
{
    int ret = drmIoctl(fd, cmd, arg);
    return ret < 0 ? -errno : ret;
}


который в свою очередь есть

Код: Выделить всё

int
drmIoctl(int fd, unsigned long request, void *arg)
{
    int    ret;

    do {
    ret = ioctl(fd, request, arg);
    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
    return ret;
}


в итоге приходим к ioctl(fd, request, arg);
Т.е. можно и без DRM обойтись, вызывая ioctl() и все будет в шоколаде.
Не пойму я только зачем все эти обертки, сотни тысяч человеко-часов потрачены на оберточную бумагу, вместо того, что бы сделать юзабельную API-конфетку с простеньким фантиком на низком уровне, и тогда бы ОС-и были бы не по 10-20 GiB, а по 50-100 MiB, как это и должно быть. Разумеется это мнение бесконечно далекое от взгляда профессионала :)
Спасибо сказали:
Аватара пользователя
Bizdelnick
Модератор
Сообщения: 21496
Статус: nulla salus bello
ОС: Debian GNU/Linux

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение Bizdelnick »

sabir писал(а):
18.06.2014 12:02
тогда бы ОС-и были бы не по 10-20 GiB, а по 50-100 MiB

Если бы один и тот же код был не в библиотеках, а повторялся в каждой программе, их общий объём был бы больше, а не меньше.
К. О.
Пишите правильно:
в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение eddy »

Если уж на то пошло, то зачем так париться, если можно просто при помощи libusb получить прямой доступ к HID-устройству и работать с ним?
Советую проверить на примере usbhid-dump (только внимательно ман прочитай: можно заблокировать клавиатуру, если запустить полный дамп всех HID-устройств).

Протокол известен. Наверняка где-то в libevdev вся эта шняга прописана, но по-моему, проще, чем ковыряться в этих толпах библиотек, реализовать свой прямой доступ.

Ну, а насчет рута - демоны обычно запускаются от рута, предоставляя пользователю общий интерфейс. Не вижу в этом проблемы.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

Bizdelnick писал(а):
18.06.2014 12:18
sabir писал(а):
18.06.2014 12:02
тогда бы ОС-и были бы не по 10-20 GiB, а по 50-100 MiB

Если бы один и тот же код был не в библиотеках, а повторялся в каждой программе, их общий объём был бы больше, а не меньше.
К. О.

Абсолютно с Вами согласен, одинаковый код ДОЛЖЕН быть в системных либах, у которых должен быть дружественный кодеру API, эти либы должны быть хорошо документированы и стандартизированы, по-русски говоря юзабельны, например libc.
Но я не об этом, а о том, что "Hello world" написанная на чистом асме и на QT или виндовом .NET/Basic - это 1 KiB против уж я не знаю сколько мегабайт (с учетом всех зависимостей разумеется) потянет модерновая среда разработки на вывод одной строки.
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

eddy писал(а):
18.06.2014 12:55
Ну, а насчет рута - демоны обычно запускаются от рута, предоставляя пользователю общий интерфейс. Не вижу в этом проблемы.

Да в общем то я так и понял, тот же Weston под юзером не запуститься ни в какую, либо под рутом, либо weston-launch, но с правами рута, либо из под systemd. Системный демон с рутовскими правами + клиенты хорошее решение.
Насчет libusb можно глянуть, покопаться, я же для удовольствия, у меня сроки не горят, даже если ничего не получиться, ну прям совсем ничего - ерунда, главное процесс творчества и созидания, а не разрушения :)
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

После некоторого гугления получился такой код:

Код: Выделить всё

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>

int main()
{
    struct input_event ev;
    const char * mouse = "/dev/input/event4";
    int fd = -1;

    fd = open(mouse, O_RDONLY);
    if(fd < 0)
    {
        fprintf(stderr, "failed to open input device %s: %s\n", mouse, strerror(errno));
        return -1;
    }

    int res;
    int x = 960;
    int y = 540;
    while(1)
    {
        res = read(fd, &ev, sizeof(struct input_event));
        if(res < 0)
        {
            fprintf(stderr, "failed to read input event from input device %s: %s\n", mouse, strerror(errno));
            close(fd);
            return -1;
        }

        if(ev.type == EV_REL)
        {
            switch(ev.code)
            {
            case REL_X:
                x = x + ev.value;
                if(x < 0) x = 0;
                if(x > 1920) x = 1920;
                fprintf(stderr, "x = %d\n", x);
            break;

            case REL_Y:
                y = y + ev.value;
                if(y < 0) y = 0;
                if(y > 1080) y = 1080;
                fprintf(stderr, "y = %d\n", y);
            break;

            default:
                break;
            }
        }
        else if(ev.type == EV_KEY)
        {
            switch(ev.code)
            {
            case BTN_LEFT:
                if(ev.value == 1) fprintf(stderr, "left button pressed\n");
                else fprintf(stderr, "left button released\n");
            break;

            case BTN_MIDDLE:
                if(ev.value == 1) fprintf(stderr, "middle button pressed\n");
                else fprintf(stderr, "middle button released\n");
            break;

            case BTN_RIGHT:
                if(ev.value == 1) fprintf(stderr, "right button pressed\n");
                else fprintf(stderr, "right button released\n");
            break;

            default:
                break;
            }
        }
    }
    close(fd);

    return 0;
}


Если мышкой двигать медленно, то все работает нормально, а если резко дернуть, то что-то где-то не успевает :(
Где косяк то, вроде все правильно или я не туда рою?
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение eddy »

sabir писал(а):
19.06.2014 13:52
Где косяк то, вроде все правильно или я не туда рою?

А точно то устройство? У меня, например, на мышу завязано 2 устройства: /dev/input/event1 и /dev/input/mouse0 (узнал о них в /dev/input/by-id).
Пощелкал. /dev/input/mouse0 выдает постоянно одно и то же.
И в консоли, и в иксах работает прилично, пропусков нет.
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

sabir писал(а):
19.06.2014 13:52
Если мышкой двигать медленно, то все работает нормально, а если резко дернуть, то что-то где-то не успевает :(
Где косяк то, вроде все правильно или я не туда рою?

Разница просто в алгоритмах, которые используются в X'ах и у Вас. Вы используете обычную линейную зависимость. А в X'ах по умолчанию ещё используется т.н. акселлерация — чем быстрее двигается мышь, тем большее расстояние проходит. Впрочем, а зачем Вам соответствие X'овой мышке, вы же её сами рисовать будете, так?
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

eddy писал(а):
19.06.2014 18:02
sabir писал(а):
19.06.2014 13:52
Где косяк то, вроде все правильно или я не туда рою?

А точно то устройство? У меня, например, на мышу завязано 2 устройства: /dev/input/event1 и /dev/input/mouse0 (узнал о них в /dev/input/by-id).
Пощелкал. /dev/input/mouse0 выдает постоянно одно и то же.
И в консоли, и в иксах работает прилично, пропусков нет.


Да устройство точно то, у меня мышка "сидит" на двух стульях, на /dev/input/event4 и /dev/input/mouse0.
Если выполнить в консоли cat /dev/input/mouse0 или cat /dev/input/event4, то реакция очень быстрая.

NickLion писал(а):
19.06.2014 21:06
Разница просто в алгоритмах, которые используются в X'ах и у Вас. Вы используете обычную линейную зависимость. А в X'ах по умолчанию ещё используется т.н. акселлерация — чем быстрее двигается мышь, тем большее расстояние проходит. Впрочем, а зачем Вам соответствие X'овой мышке, вы же её сами рисовать будете, так?

Похоже в алгоритме аселерации то все и дело. Главное было понять, что я рою в правильном месте, иксы тоже считывают их /dev/input/*. Соответствие иксовой мышке мне действительно не нужно, как впрочем и сами иксы, сам буду рисовать курсор :)
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

sabir писал(а):
19.06.2014 23:34
Похоже в алгоритме аселерации то все и дело. Главное было понять, что я рою в правильном месте, иксы тоже считывают их /dev/input/*. Соответствие иксовой мышке мне действительно не нужно, как впрочем и сами иксы, сам буду рисовать курсор :)


Изменил две строчки в коде и все стало работать вполне адекватно.
Было:

Код: Выделить всё

x = x + ev.value;
y = y + ev.value;

Стало:

Код: Выделить всё

x = x + ev.value * 2;
y = y + ev.value * 2;

Вживую можно будет подобрать коэффициент акселерации, в зависимости от ширины и высоты дисплея, его разрешения, скорости перемещения мышки и чего-нибудь еще, там видно будет.
Спасибо NickLion, очень выручили :)
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

Правильнее будет сделать что-то вроде:

Код: Выделить всё

if (rel < 30)
    x += rel;
else if (rel < 100)
    x += rel * 3 / 2;
else
    x += rel * 2;

Количество таких уровней — не знаю. Тут я 3 привёл, может 2 будет достаточно. Коэффициенты и пороги — с потолка взял. Просто в Вашем случае не получится при медленном движении точно на пиксели попадать.
Спасибо сказали:
Аватара пользователя
eddy
Сообщения: 3321
Статус: Красный глаз тролля
ОС: ArchLinux

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение eddy »

NickLion, да можно просто и честно сделать - безо всяких уровней, по формуле вроде

Код: Выделить всё

x += (rel*(1 + rel))/2;

Код: Выделить всё

rel   dx
0     0
1     1
2     3
5     15
100  5050
RTFM
-------
KOI8-R - патриотичная кодировка Изображение
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

eddy
Да, можно, но квадрат, как мне кажется, слишком быстро растёт. Тут лучше степень поменьше взять. Приплетать сопроцессор на каждое движение мыши, имхо, как-то растратно, поэтому или приближённо считать, или mmx, или просто порогами.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

В общем, в xorg'е используется простая схема, если не превышает порог, то как есть, если больше, то умножить на коэффициент и разделить на второй коэффициент. Узнать параметры:

xset q | grep -A1 Pointer

Pointer Control: acceleration: 20/10 threshold: 4


Подставил в программу:

Код: Выделить всё

(ev.value <= 4 && ev.value >= -4 ? ev.value : ev.value * 2)

Вместо просто ev.value, поменял условие

Код: Выделить всё

if(x >= SCREEN_WIDTH) x = SCREEN_WIDTH - 1;
if(y >= SCREEN_HEIGHT) y = SCREEN_HEIGHT - 1;

Чтобы не выходило за диапазон, совпадает с xorg.
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

NickLion писал(а):
20.06.2014 09:49
В общем, в xorg'е используется простая схема, если не превышает порог, то как есть, если больше, то умножить на коэффициент и разделить на второй коэффициент. Узнать параметры:

xset q | grep -A1 Pointer

Pointer Control: acceleration: 20/10 threshold: 4


Подставил в программу:

Код: Выделить всё

(ev.value <= 4 && ev.value >= -4 ? ev.value : ev.value * 2)

Вместо просто ev.value, поменял условие

Код: Выделить всё

if(x >= SCREEN_WIDTH) x = SCREEN_WIDTH - 1;
if(y >= SCREEN_HEIGHT) y = SCREEN_HEIGHT - 1;

Чтобы не выходило за диапазон, совпадает с xorg.

eddy, NickLion спасибо парни за помощь, курсор уже могу рисовать из PNG c учетом "прозрачных" пикселей, мышка бегает по экрану вроде хорошо, но поскольку писал ночью на скорую руку, лишь бы запустилось, то проверок на ошибки нет, частенько при выскакивании за пределы экрана выпадает sefmentation fault, но это фигня, просто пытаюсь рисовать за пределы видео буффера, лечиться проверками на валидность значения, главное работает :)
Еще вопрос есть, поскольку это будет клиент-серверная мулька, то как лучше организовать обмен данными между клиентом и сервером?
Со стороны сервера будут идти только event-ы типа int, ну типа нужно перерисовать содержание окна или юзер щелкнул мышкой/клавой, кнопка правая, координаты такие и прочее.
Задачей сервера будет:
- создание окна с заданными свойствами, возможность изменения свойств, перемещение окна, изменение его размеров, убийство окна, управление фокусом (активное/не активное)
- реакция на нажатие кнопок закрытия, развертывания и сворачивания окна, двойной клик по шапке
- плюс сервер должен будет прорисовывать окна с учетом прозрачности, при их наложении друг на друга
- работа с мышью и клавой разумеется, ну и прочую работу оконного менеджера
Причем сервер будет предоставлять клиенту только фрейм с пустой клиентской областью, а уже задачей клиента будет рисовать в клиентской части что-то по возможности полезное.
Так что от клиентов будет идти постоянный и не хилый поток графической информации о содержании окон.
Как лучше сделать, через сокеты или можно(?) создать виртуальное устройство в том же /dev/* и читать/писать read/write, а еще бы лучше обмен данными через расшаренную память, может ли сервер выделить кусок памяти, что бы клиенты могли передавать туда данные?
Где бы раздобыть правильную доку по libpixman, libpng и libjpeg на русском, по пиндосски я нашел, но понимаю плохенько. Читать png и jpeg я уже умею, меня интересует исключительно возможность отскалить имидж средствами самих libpng и libjpeg, последний вроде как даже это умеет, при условии кратности 2 или 4, но примеров как это сделать найти не удалось, все юзают gdk-pixbuf, cairo или на крайняк imlib2, оно и понятно, сроки, время-деньги, ну а для души охота что-то по-низкоуровнее :) Сорцы смотрел, но мозгов не хватает, уж больно мудрено пишут, чем разбираться в чужом проще и быстрее написать свое, в разумных пределах конечно, писать с нуля FarCry 4 для линя я конечно не собираюсь :)
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

Методом тыка вывелась рабочая схема:

Код: Выделить всё

if(ev.type == EV_REL)
        {
            switch(ev.code)
            {
            case REL_X:
                if(ev.value <= 2 && ev.value >= -2) x += ev.value;
                else if(ev.value <= 4 && ev.value >= -4) x += ev.value * 3 / 2;
                else if(ev.value <= 16 && ev.value >= -16) x += ev.value * 2;
                else x += ev.value * 3;
                if(x < 0) x = 0;
                if(x >= dsp->width) x = dsp->width - 1;
            break;

            case REL_Y:
                if(ev.value <= 2 && ev.value >= -2) y += ev.value;
                else if(ev.value <= 4 && ev.value >= -4) y += ev.value * 3 / 2;
                else if(ev.value <= 16 && ev.value >= -16) y += ev.value * 2;
                else y += ev.value * 3;
                if(y < 0) y = 0;
                if(y >= dsp->height) y = dsp->height - 1;
            break;

            default:
                break;
            }
        }

Мышка бегает хорошо, плавно, как надо в общем.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

Можно UNIX Domain socket использовать. Хотя и loopback нормально. Всё равно они через shared memory, вроде, организовываются. Можете и shared memory, но это немного сложнее. Но передачу кадра можно будет просто организовать как общую память, просто писать туда и сигналить, кода что-то поменялось.

По поводу масшатбирования — там много нюансов. Точнее много методов, у которых эти нюансы есть. Ну, и отличия на растягивание и сжатие.
Ближайший сосед — простейший метод. Идём по (x, y) результирующего изображения, считаем (x', y') исходного, берём цвет и пишем его. Ускорение — не делить/умножать для каждого пикселя, а суммировать. Можно даже оставаясь в целых числах.
Линейная интерполяция. Аналогично выше, но если для пикселя получили x' c дробной частью, то учитываем 2 пикселя. Аналогично для y'. Т.е. всего 4 пикселя можем использовать. Например, x' =3.2; y' = 4.7; тогда берём в качестве цвета:
C(x,y)=C'(3, 4) * (1 - 0.2) * (1 - 0.7) + C'(4, 4) * (0.2) * (1 - 0.7) + C'(3, 5) * (1 - 0.2) * (0.7) + C'(4, 5) * (0.2) * (0.7)
Бикубическая уже учитывает по 4 пикселя по горизонтали и вертикали, итого: 16 пикселей.

PS лучше x', y' со сдвигом на -0.5 сразу считать.

PPS это на растяжение. на сжатие надо суммировать цвета пикселей в зоне с учётом попадания на границу.
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

NickLion писал(а):
20.06.2014 16:18
Можно UNIX Domain socket использовать. Хотя и loopback нормально. Всё равно они через shared memory, вроде, организовываются. Можете и shared memory, но это немного сложнее. Но передачу кадра можно будет просто организовать как общую память, просто писать туда и сигналить, кода что-то поменялось.

Нарыл еще в инете инфы про tmpfs, Можно откусить кусок памяти и примонтировать его, что то типа:

Код: Выделить всё

mount -t tmpfs -o size=256M tmpfs /tmp/ramdisk/

Все операции будут идти прямо в памяти, HDD не требуется => и скорость обмена данными будет ограничена только скоростью работы памяти, или это тупиковое направление?
NickLion писал(а):
20.06.2014 16:18
По поводу масшатбирования — там много нюансов. Точнее много методов, у которых эти нюансы есть. Ну, и отличия на растягивание и сжатие.
Ближайший сосед — простейший метод. Идём по (x, y) результирующего изображения, считаем (x', y') исходного, берём цвет и пишем его. Ускорение — не делить/умножать для каждого пикселя, а суммировать. Можно даже оставаясь в целых числах.
Линейная интерполяция. Аналогично выше, но если для пикселя получили x' c дробной частью, то учитываем 2 пикселя. Аналогично для y'. Т.е. всего 4 пикселя можем использовать. Например, x' =3.2; y' = 4.7; тогда берём в качестве цвета:
C(x,y)=C'(3, 4) * (1 - 0.2) * (1 - 0.7) + C'(4, 4) * (0.2) * (1 - 0.7) + C'(3, 5) * (1 - 0.2) * (0.7) + C'(4, 5) * (0.2) * (0.7)
Бикубическая уже учитывает по 4 пикселя по горизонтали и вертикали, итого: 16 пикселей.

PS лучше x', y' со сдвигом на -0.5 сразу считать.

PPS это на растяжение. на сжатие надо суммировать цвета пикселей в зоне с учётом попадания на границу.

У Вас нет простого примерчика в загашнике, мне не надо сильно продвинутых алгоритмов, идеи написать свой фотошоп или гимп нет, надо всего лишь отскалить обои под размер рабочего стола, да иконки в 1.5-2 раза зажать-разжать.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

Вот код сваял. Там Qt для проверки, что всё норм, но там легко поменять для нужного формата.
scaleNearestNeighbour — по методу ближайшего, как для сжатия (пиксели выпадают), так и растяжения (квадратиками)
scaleBilinear — билинейный (учтены ограничения по выходу за диапазон, поэтому немного страшно выглядит и 4 раза расчёт производится. Данный методы нормально работает на растяжение, а на сжатие только чуть меньше 1 нормально работает (где-то до 0.75), а меньше начинает пропускать пиксели как и ближайший сосед, там надо немного по-другому делать.
.width() — ширина изображения
.height() — высота
.scanLine(i) — адрес строки i изображения
И да, методы расчитаны толко на 32-х битные изображения!

Код: Выделить всё

QImage scaleNearestNeighbour(const QImage& img1, double xFactor, double yFactor)
{
    if (img1.format() != QImage::Format_RGB32 && img1.format() != QImage::Format_ARGB32)
        return QImage();
    int nw = img1.width() * xFactor;
    int nh = img1.height() * yFactor;
    QImage img2(nw, nh, img1.format());
    for (int y = 0; y < nh; y++) {
        double y_ = y / yFactor;
        double x_ = 0;
        const quint32* p1 = reinterpret_cast<const quint32*>(img1.scanLine((int)y_));
        quint32* p2 = reinterpret_cast<quint32*>(img2.scanLine(y));
        double dx = 1.0 / xFactor;
        for (int x = 0; x < nw; x++, p2++, x_ += dx) {
            *p2 = p1[(int)x_];
        }
    }
    return img2;
}

QImage scaleBilinear(const QImage& img1, double xFactor, double yFactor)
{
    if (img1.format() != QImage::Format_RGB32 && img1.format() != QImage::Format_ARGB32)
        return QImage();
    int nw = img1.width() * xFactor;
    int nh = img1.height() * yFactor;
    QImage img2(nw, nh, img1.format());
    for (int y = 0; y < nh; y++) {
        double y_ = (y + 0.5) / yFactor - 0.5;
        double x_ = 0.5 / xFactor - 0.5;
        const uchar* p10 = img1.scanLine((int)y_);
        const uchar* p11 = img1.scanLine((int)y_ + 1);
        uchar* p2 = img2.scanLine(y);
        double dx = 1.0 / xFactor;
        int yi = (int)y_;
        double yf = y_ - yi;

        if (yf >= 0 && yi < img1.height() - 1) {
            for (int x = 0; x < nw; x++, x_ += dx) {
                int xi = (int)x_;
                double xf = x_ - xi;
                if (xf >= 0 && xi < img1.width() - 1) {
                    for (int c = 0; c < 4; c++, p2++) {
                        *p2 = (p10[xi * 4 + c] + (p10[xi * 4 + 4 + c] - p10[xi * 4 + c]) * xf) * (1 - yf)
                                + (p11[xi * 4 + c] + (p11[xi * 4 + 4 + c] - p11[xi * 4 + c]) * xf) * yf;
                    }
                } else {
                    for (int c = 0; c < 4; c++, p2++) {
                        *p2 = p10[xi * 4 + c] * (1 - yf)
                                + p11[xi * 4 + c] * yf;
                    }
                }
            }
        } else {
            for (int x = 0; x < nw; x++, x_ += dx) {
                int xi = (int)x_;
                double xf = x_ - xi;
                if (xf >= 0 && xi < img1.width() - 1) {
                    for (int c = 0; c < 4; c++, p2++) {
                        *p2 = p10[xi * 4 + c] + (p10[xi * 4 + 4 + c] - p10[xi * 4 + c]) * xf;
                    }
                } else {
                    for (int c = 0; c < 4; c++, p2++) {
                        *p2 = p10[xi * 4 + c];
                    }
                }
            }
        }
    }
    return img2;
}
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

NickLion писал(а):
22.06.2014 11:19
Вот код сваял. Там Qt для проверки, что всё норм, но там легко поменять для нужного формата.
scaleNearestNeighbour — по методу ближайшего, как для сжатия (пиксели выпадают), так и растяжения (квадратиками)
scaleBilinear — билинейный (учтены ограничения по выходу за диапазон, поэтому немного страшно выглядит и 4 раза расчёт производится. Данный методы нормально работает на растяжение, а на сжатие только чуть меньше 1 нормально работает (где-то до 0.75), а меньше начинает пропускать пиксели как и ближайший сосед, там надо немного по-другому делать.
.width() — ширина изображения
.height() — высота
.scanLine(i) — адрес строки i изображения
И да, методы расчитаны толко на 32-х битные изображения!

Благодарю Вас NickLion, попробую прикрутить.
Сегодня полночи просидел в инете, пытался понять теорию. С методом ближайшего соседа разобрался, это совсем просто, а вот с билинейной и бикубической интерполяцией беда. Не пойму я принципа как их расчитывают.

Задача заключается в нахождении яркости в точке V.
Сначала находится яркость в точке V5 с учетом линейной интерполяции между точками V1 и V4; затем в точке V6 с учетом интерполяции между точками V2 и V3. Затем производится интерполяция между точками V5 и V6 для нахождения яркости в точке V.
Изображение
V5=V1*(1-Ex) +V4*Ex

V6=V2*(1-Ex) +V3*Ex

V = V5*(1-Ey) +V6*Ey

Ex, EyО{0:1}
Мне все здесь понятно, кроме одного, почему интервал {0:1}, ведь расстояние между соседними пикселями всегда = 1, они же рядом, откуда беруться дробные числа.

Вот простой пример, есть два пикселя, между ними нужно вставить третий. Я думаю что цвет среднего (вставляемого) пикселя должен быть: (пиксель №1 + пиксель №2) : 2 = пиксель №3
(200 + 100) : 2 = 150, т.е. среднее значение, это то или нет, если то, то откуда здесь взяться интервалу {0:1}

Или вот Билинейная интерполяция:
Изображение
Четыре красные точки представляют собой известные значения функции. Значение в зеленой точке должно быть интерполировано.
Пример билинейной интерполяции в единичном квадрате. Значения вершин составляют 0, 1, 1 и 0.5. Опять единичный квадрат, почему единичный, не догоняю.

Ниже приведен пример программы билинейной интерполяции изображения, написанный на C99 (на C89 компилироваться не будет!) (стырено с википедии(ц))
Входные параметры:

a - указатель на массив пикселей изображения, которое необходимо увеличить (уменьшить)
Нумерация элементов [0..old_h-1, 0..old_w-1]
oldw - старая ширина изображения
oldh - старая высота изображения

Выходные параметры:

b - указатель на массив пикселей ресемплированного изображения
Нумерация элементов [0..new_h-1, 0..new_w-1]
neww - новая ширина изображения
newh - новая высота изображения

Код: Выделить всё

#include <stdio.h>
#include <math.h>
#include <sys/types.h>

void resample(int oldw, int oldh, int neww, int newh, u_int a[oldh][oldw], u_int b[newh][neww])
{
    int i, j;
    int h, w;
    float t;
    float u;
    float tmp;
    float d1, d2, d3, d4;
    u_int p1, p2, p3, p4;    /* Окрестные пикселы */

    u_char red, green, blue;

    for (j = 0; j < newh; j++) {
            tmp = (float) (j) / (float) (newh - 1) * (oldh - 1);
        h = (int) floor(tmp);
        if (h < 0) {
        h = 0;
        } else {
        if (h >= oldh - 1) {
            h = oldh - 2;
        }
        }
        u = tmp - h;

    for (i = 0; i < neww; i++) {

        tmp = (float) (i) / (float) (neww - 1) * (oldw - 1);
        w = (int) floor(tmp);
        if (w < 0) {
        w = 0;
        } else {
        if (w >= oldw - 1) {
            w = oldw - 2;
        }
        }
        t = tmp - w;

        /* Коэффициенты */
        d1 = (1 - t) * (1 - u);
        d2 = t * (1 - u);
        d3 = t * u;
        d4 = (1 - t) * u;

        /* Окрестные пиксели: a[i][j] */
        p1 = a[h][w];
        p2 = a[h][w + 1];
        p3 = a[h + 1][w + 1];
        p4 = a[h + 1][w];

        /* Компоненты */
        blue = (u_char) p1 *d1 + (u_char) p2 *d2 + (u_char) p3 *d3 + (u_char) p4 *d4;
        green = (u_char) (p1 >> 8) * d1 + (u_char) (p2 >> 8) * d2 + (u_char) (p3 >> 8) * d3 + (u_char) (p4 >> 8) * d4;
        red = (u_char) (p1 >> 16) * d1 + (u_char) (p2 >> 16) * d2 + (u_char) (p3 >> 16) * d3 + (u_char) (p4 >> 16) * d4;

        /* Новый пиксел из R G B  */
        b[j][i] = ((u_int32_t) red << 16) | ((u_int32_t) green << 8) | (blue);
    }


Пытался по тупому вставить, компилируется, но не работает,
a - указатель на массив пикселей изображения, которое необходимо увеличить (уменьшить)
b - указатель на массив пикселей ресемплированного изображения
Я передаю в качестве аргумента int *a и int *b, то есть два массива, один с загруженным изображением, второй пустой

Код: Выделить всё

int *img = img_read_jpeg("/usr/share/wallpapers/wallpaper.jpg"); // размер 1280х1018, функция возвращает int *imge, на мой взгляд это указатель на массив пикселей типа int, (int r, g, b), или я туплю?
int *img_new = calloc(1920 * 1080, 4); // выделяю память под массив, куда будет выход данных
// вызываю функцию
resample(1280, 1018, 1920, 1080, img, img_new);

Компилируется, с предупреждениями:

Код: Выделить всё

foxy.c:445:3: warning: passing argument 5 of ‘resample’ from incompatible pointer type [enabled by default]
resample(1280, 1018, 1920, 1080, img, img_new);
foxy.c:327:6: note: expected ‘u_int (*)[(sizetype)(oldw)]’ but argument is of type ‘int *’
void resample(int oldw, int oldh, int neww, int newh, u_int a[oldh][oldw], u_int b[newh][neww])
foxy.c:445:3: warning: passing argument 6 of ‘resample’ from incompatible pointer type [enabled by default]
resample(1280, 1018, 1920, 1080, img, img_new);
foxy.c:327:6: note: expected ‘u_int (*)[(sizetype)(neww)]’ but argument is of type ‘u_int *’
void resample(int oldw, int oldh, int neww, int newh, u_int a[oldh][oldw], u_int b[newh][neww])

Понимаю, что проблема с не соответствием типов int vs u_int или что то вроде этого, что не так, где косяк (мой конечно). Не люблю я все эти типы u_int и прочие, то ли дело в асме BYTE, WORD, DWORD, QWORD и все, а больше ничего и не надо, удобно, понятно, просто, а в сях столько типов, мозг клинит, не говоря уже про срр и прочие ЯВУ.
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение NickLion »

Дробные расстояния возникают после увеличения картинки. Если увеличить картинку в 3 раза, то расстояния пикслеей исходной картинки станет 3 пикселя в результирующей. Пэтому, когда берутся целые координаты в результирующей, то на исходной попадаем между пикселями.
Код, который приведен также расчитан только на 32-х битный цвет. Что за img_read_jpeg — я не знаю. Возможно, он возвращает 24-х битную картинку, а ожидается 32-х.
Спасибо сказали:
Аватара пользователя
sabir
Сообщения: 66
ОС: OpenBSD

Re: DRM: рисовать в видеобуфер без Xorg

Сообщение sabir »

NickLion писал(а):
22.06.2014 16:03
Дробные расстояния возникают после увеличения картинки. Если увеличить картинку в 3 раза, то расстояния пикслеей исходной картинки станет 3 пикселя в результирующей. Пэтому, когда берутся целые координаты в результирующей, то на исходной попадаем между пикселями.
Код, который приведен также расчитан только на 32-х битный цвет. Что за img_read_jpeg — я не знаю. Возможно, он возвращает 24-х битную картинку, а ожидается 32-х.

OK буду разбираться :)
Спасибо сказали: