Initial version
authorTomi Valkeinen <tomi.valkeinen@iki.fi>
Sun, 27 Sep 2015 22:13:34 +0000 (01:13 +0300)
committerTomi Valkeinen <tomi.valkeinen@iki.fi>
Sun, 27 Sep 2015 22:13:34 +0000 (01:13 +0300)
44 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
db/CMakeLists.txt [new file with mode: 0644]
db/db.cpp [new file with mode: 0644]
libkms++/CMakeLists.txt [new file with mode: 0644]
libkms++/atomicreq.cpp [new file with mode: 0644]
libkms++/atomicreq.h [new file with mode: 0644]
libkms++/card.cpp [new file with mode: 0644]
libkms++/card.h [new file with mode: 0644]
libkms++/connector.cpp [new file with mode: 0644]
libkms++/connector.h [new file with mode: 0644]
libkms++/crtc.cpp [new file with mode: 0644]
libkms++/crtc.h [new file with mode: 0644]
libkms++/decls.h [new file with mode: 0644]
libkms++/drmobject.cpp [new file with mode: 0644]
libkms++/drmobject.h [new file with mode: 0644]
libkms++/encoder.cpp [new file with mode: 0644]
libkms++/encoder.h [new file with mode: 0644]
libkms++/framebuffer.cpp [new file with mode: 0644]
libkms++/framebuffer.h [new file with mode: 0644]
libkms++/helpers.cpp [new file with mode: 0644]
libkms++/helpers.h [new file with mode: 0644]
libkms++/kms++.h [new file with mode: 0644]
libkms++/plane.cpp [new file with mode: 0644]
libkms++/plane.h [new file with mode: 0644]
libkms++/property.cpp [new file with mode: 0644]
libkms++/property.h [new file with mode: 0644]
libkms++/utils/color.cpp [new file with mode: 0644]
libkms++/utils/color.h [new file with mode: 0644]
libkms++/utils/conv.cpp [new file with mode: 0644]
libkms++/utils/conv.h [new file with mode: 0644]
libkms++/utils/testpat.cpp [new file with mode: 0644]
libkms++/utils/testpat.h [new file with mode: 0644]
lua/CMakeLists.txt [new file with mode: 0644]
lua/luakms.i [new file with mode: 0644]
lua/test.lua [new file with mode: 0755]
py/CMakeLists.txt [new file with mode: 0644]
py/functest.py [new file with mode: 0755]
py/pykms.i [new file with mode: 0644]
py/test.py [new file with mode: 0755]
test.h [new file with mode: 0644]
testpat/CMakeLists.txt [new file with mode: 0644]
testpat/testpat.cpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3565c82
--- /dev/null
@@ -0,0 +1,6 @@
+*.swp
+*.a
+*.so
+build
+*.txt.user
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..060dd7b
--- /dev/null
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.0)
+project(kms++)
+
+IF(NOT CMAKE_BUILD_TYPE)
+  SET(CMAKE_BUILD_TYPE Release CACHE STRING
+      "Choose the type of build, options are: Debug, Release."
+      FORCE)
+ENDIF(NOT CMAKE_BUILD_TYPE)
+
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
+
+set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
+
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(LIBDRM libdrm REQUIRED)
+
+enable_testing()
+
+add_subdirectory(libkms++)
+add_subdirectory(testpat)
+add_subdirectory(db)
+add_subdirectory(py)
+add_subdirectory(lua)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..4df798d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,8 @@
+libkms++ and test tools
+
+Copyright 2015 Tomi Valkeinen
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt
new file mode 100644 (file)
index 0000000..91ab888
--- /dev/null
@@ -0,0 +1,6 @@
+include_directories(${LIBDRM_INCLUDE_DIRS})
+link_directories(${LIBDRM_LIBRARY_DIRS})
+
+add_executable (db db.cpp)
+
+target_link_libraries(db kms++ ${LIBDRM_LIBRARIES})
diff --git a/db/db.cpp b/db/db.cpp
new file mode 100644 (file)
index 0000000..d119078
--- /dev/null
+++ b/db/db.cpp
@@ -0,0 +1,309 @@
+#include <cstdio>
+#include <algorithm>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "kms++.h"
+#include "utils/color.h"
+
+#include "../test.h"
+
+using namespace std;
+using namespace kms;
+
+static void draw_color_bar(Framebuffer& buf, int old_xpos, int xpos, int width);
+
+static void main_loop(Card& card);
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+struct Output
+{
+       Connector* connector;
+       Crtc* crtc;
+       Framebuffer* fbs[2];
+
+       int front_buf;
+       int bar_xpos;
+};
+
+static void do_flip(Output* out);
+
+int main()
+{
+       Card card;
+
+       if (card.master() == false)
+               printf("Not DRM master, modeset may fail\n");
+
+       //card.print_short();
+
+       vector<Output> outputs;
+
+       for (auto conn : card.get_connectors())
+       {
+               if (conn->connected() == false)
+                       continue;
+
+               auto mode = conn->get_default_mode();
+
+               Crtc* crtc = conn->get_current_crtc();
+               if (!crtc) {
+                       for (auto c : conn->get_possible_crtcs()) {
+                               if (find_if(outputs.begin(), outputs.end(), [c](Output o) { return o.crtc == c; }) == outputs.end()) {
+                                       crtc = c;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!crtc) {
+                       printf("failed to find crtc\n");
+                       return -1;
+               }
+
+               auto fb1 = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+               auto fb2 = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+
+               printf("conn %u, crtc %u, fb1 %u, fb2 %u\n", conn->id(), crtc->id(), fb1->id(), fb2->id());
+
+               Output output = { };
+               output.connector = conn;
+               output.crtc = crtc;
+               output.fbs[0] = fb1;
+               output.fbs[1] = fb2;
+               outputs.push_back(output);
+       }
+
+       for(auto& out : outputs) {
+               auto conn = out.connector;
+               auto crtc = out.crtc;
+
+               auto mode = conn->get_default_mode();
+               int r = crtc->set_mode(conn, *out.fbs[0], mode);
+               ASSERT(r == 0);
+       }
+
+       for(auto& out : outputs)
+               do_flip(&out);
+
+       main_loop(card);
+
+       for(auto& out : outputs) {
+               delete out.fbs[0];
+               delete out.fbs[1];
+       }
+}
+
+static void do_flip(Output* out)
+{
+       const int bar_width = 20;
+       const int bar_speed = 8;
+
+       auto crtc = out->crtc;
+       auto fb = out->fbs[(out->front_buf + 1) % 2];
+
+       ASSERT(crtc);
+       ASSERT(fb);
+
+       int current_xpos = out->bar_xpos;
+       int old_xpos = (current_xpos + (fb->width() - bar_width - bar_speed)) % (fb->width() - bar_width);
+       int new_xpos = (current_xpos + bar_speed) % (fb->width() - bar_width);
+
+       draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
+
+       out->bar_xpos = new_xpos;
+
+       auto& card = crtc->card();
+
+       if (card.has_atomic()) {
+               int r;
+
+               AtomicReq ctx(card);
+
+               // XXX
+               //ctx.add(plane, card.get_prop("CRTC_X"), 50);
+               //ctx.add(plane, card.get_prop("CRTC_Y"), 50);
+
+               r = ctx.test();
+               ASSERT(r == 0);
+
+               r = ctx.commit();
+               ASSERT(r == 0);
+       } else {
+               int r = drmModePageFlip(card.fd(), crtc->id(), fb->id(), DRM_MODE_PAGE_FLIP_EVENT, out);
+               ASSERT(r == 0);
+       }
+}
+
+static void page_flip_handler(int fd, unsigned int frame,
+                             unsigned int sec, unsigned int usec,
+                             void *data)
+{
+       //printf("flip event %d, %d, %u, %u, %p\n", fd, frame, sec, usec, data);
+
+       auto out = (Output*)data;
+
+       out->front_buf = (out->front_buf + 1) % 2;
+
+       do_flip(out);
+}
+
+
+static void main_loop(Card& card)
+{
+       drmEventContext ev = {
+               .version = DRM_EVENT_CONTEXT_VERSION,
+               .vblank_handler = 0,
+               .page_flip_handler = page_flip_handler,
+       };
+
+       fd_set fds;
+
+       FD_ZERO(&fds);
+
+       int fd = card.fd();
+
+       printf("press enter to exit\n");
+
+       while (true) {
+               int r;
+
+               FD_SET(0, &fds);
+               FD_SET(fd, &fds);
+
+               r = select(fd + 1, &fds, NULL, NULL, NULL);
+               if (r < 0) {
+                       fprintf(stderr, "select() failed with %d: %m\n", errno);
+                       break;
+               } else if (FD_ISSET(0, &fds)) {
+                       fprintf(stderr, "exit due to user-input\n");
+                       break;
+               } else if (FD_ISSET(fd, &fds)) {
+                       drmHandleEvent(fd, &ev);
+               }
+       }
+}
+
+
+static const RGB colors32[] = {
+       RGB(255, 255, 255),
+       RGB(255, 0, 0),
+       RGB(255, 255, 255),
+       RGB(0, 255, 0),
+       RGB(255, 255, 255),
+       RGB(0, 0, 255),
+       RGB(255, 255, 255),
+       RGB(200, 200, 200),
+       RGB(255, 255, 255),
+       RGB(100, 100, 100),
+       RGB(255, 255, 255),
+       RGB(50, 50, 50),
+};
+
+static const uint16_t colors16[] = {
+       colors32[0].rgb565(),
+       colors32[1].rgb565(),
+       colors32[2].rgb565(),
+       colors32[3].rgb565(),
+       colors32[4].rgb565(),
+       colors32[5].rgb565(),
+       colors32[6].rgb565(),
+       colors32[7].rgb565(),
+       colors32[8].rgb565(),
+       colors32[9].rgb565(),
+       colors32[10].rgb565(),
+       colors32[11].rgb565(),
+};
+
+static void drm_draw_color_bar_rgb888(Framebuffer& buf, int old_xpos, int xpos, int width)
+{
+       for (unsigned y = 0; y < buf.height(); ++y) {
+               RGB bcol = colors32[y * ARRAY_SIZE(colors32) / buf.height()];
+               uint32_t *line = (uint32_t*)(buf.map(0) + buf.stride(0) * y);
+
+               if (old_xpos >= 0) {
+                       for (int x = old_xpos; x < old_xpos + width; ++x)
+                               line[x] = 0;
+               }
+
+               for (int x = xpos; x < xpos + width; ++x)
+                       line[x] = bcol.raw;
+       }
+}
+
+static void drm_draw_color_bar_rgb565(Framebuffer& buf, int old_xpos, int xpos, int width)
+{
+       static_assert(ARRAY_SIZE(colors32) == ARRAY_SIZE(colors16), "bad colors arrays");
+
+       for (unsigned y = 0; y < buf.height(); ++y) {
+               uint16_t bcol = colors16[y * ARRAY_SIZE(colors16) / buf.height()];
+               uint16_t *line = (uint16_t*)(buf.map(0) + buf.stride(0) * y);
+
+               if (old_xpos >= 0) {
+                       for (int x = old_xpos; x < old_xpos + width; ++x)
+                               line[x] = 0;
+               }
+
+               for (int x = xpos; x < xpos + width; ++x)
+                       line[x] = bcol;
+       }
+}
+
+static void drm_draw_color_bar_semiplanar_yuv(Framebuffer& buf, int old_xpos, int xpos, int width)
+{
+       const uint8_t colors[] = {
+               0xff,
+               0x00,
+               0xff,
+               0x20,
+               0xff,
+               0x40,
+               0xff,
+               0x80,
+               0xff,
+       };
+
+       for (unsigned y = 0; y < buf.height(); ++y) {
+               unsigned int bcol = colors[y * ARRAY_SIZE(colors) / buf.height()];
+               uint8_t *line = (uint8_t*)(buf.map(0) + buf.stride(0) * y);
+
+               if (old_xpos >= 0) {
+                       for (int x = old_xpos; x < old_xpos + width; ++x)
+                               line[x] = 0;
+               }
+
+               for (int x = xpos; x < xpos + width; ++x)
+                       line[x] = bcol;
+       }
+}
+
+static void draw_color_bar(Framebuffer& buf, int old_xpos, int xpos, int width)
+{
+       switch (buf.format()) {
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+               // XXX not right but gets something on the screen
+               drm_draw_color_bar_semiplanar_yuv(buf, old_xpos, xpos, width);
+               break;
+
+       case DRM_FORMAT_YUYV:
+       case DRM_FORMAT_UYVY:
+               // XXX not right but gets something on the screen
+               drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width);
+               break;
+
+       case DRM_FORMAT_RGB565:
+               drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width);
+               break;
+
+       case DRM_FORMAT_XRGB8888:
+               drm_draw_color_bar_rgb888(buf, old_xpos, xpos, width);
+               break;
+
+       default:
+               ASSERT(false);
+       }
+}
diff --git a/libkms++/CMakeLists.txt b/libkms++/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e8d14f7
--- /dev/null
@@ -0,0 +1,7 @@
+include_directories(${LIBDRM_INCLUDE_DIRS})
+link_directories(${LIBDRM_LIBRARY_DIRS})
+
+file(GLOB SRCS "*.cpp" "*.h" "utils/*.cpp" "utils/*.h")
+add_library(kms++ ${SRCS})
+
+target_include_directories(kms++ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/libkms++/atomicreq.cpp b/libkms++/atomicreq.cpp
new file mode 100644 (file)
index 0000000..3346af9
--- /dev/null
@@ -0,0 +1,64 @@
+#include <cassert>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "atomicreq.h"
+#include "property.h"
+#include "card.h"
+
+#ifndef DRM_CLIENT_CAP_ATOMIC
+
+#define DRM_MODE_ATOMIC_TEST_ONLY 0
+
+struct _drmModeAtomicReq;
+typedef struct _drmModeAtomicReq* drmModeAtomicReqPtr;
+
+static inline drmModeAtomicReqPtr drmModeAtomicAlloc() { return 0; }
+static inline void drmModeAtomicFree(drmModeAtomicReqPtr) { }
+static inline int drmModeAtomicAddProperty(drmModeAtomicReqPtr, uint32_t, uint32_t, uint64_t) { return 0; }
+static inline int drmModeAtomicCommit(int, drmModeAtomicReqPtr, int, void*) { return 0; }
+
+#endif // DRM_CLIENT_CAP_ATOMIC
+
+namespace kms
+{
+AtomicReq::AtomicReq(Card& card)
+       : m_card(card)
+{
+       assert(card.has_atomic());
+       m_req = drmModeAtomicAlloc();
+}
+
+AtomicReq::~AtomicReq()
+{
+       drmModeAtomicFree(m_req);
+}
+
+void AtomicReq::add(uint32_t ob_id, uint32_t prop_id, uint64_t value)
+{
+       int r = drmModeAtomicAddProperty(m_req, ob_id, prop_id, value);
+       if (r <= 0)
+               throw std::invalid_argument("foo");
+}
+
+void AtomicReq::add(DrmObject *ob, Property *prop, uint64_t value)
+{
+       add(ob->id(), prop->id(), value);
+}
+
+int AtomicReq::test()
+{
+       uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY;
+
+       return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0);
+}
+
+int AtomicReq::commit()
+{
+       uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
+       void* data = 0;
+
+       return drmModeAtomicCommit(m_card.fd(), m_req, flags, data);
+}
+}
diff --git a/libkms++/atomicreq.h b/libkms++/atomicreq.h
new file mode 100644 (file)
index 0000000..9a8a748
--- /dev/null
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <cstdint>
+
+struct _drmModeAtomicReq;
+
+#include "decls.h"
+
+namespace kms
+{
+class AtomicReq
+{
+public:
+       AtomicReq(Card& card);
+       ~AtomicReq();
+
+       AtomicReq(const AtomicReq& other) = delete;
+       AtomicReq& operator=(const AtomicReq& other) = delete;
+
+       void add(uint32_t ob_id, uint32_t prop_id, uint64_t value);
+       void add(DrmObject *ob, Property *prop, uint64_t value);
+
+       int test();
+       int commit();
+
+private:
+       Card& m_card;
+       _drmModeAtomicReq* m_req;
+};
+
+}
diff --git a/libkms++/card.cpp b/libkms++/card.cpp
new file mode 100644 (file)
index 0000000..2ba350d
--- /dev/null
@@ -0,0 +1,198 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utility>
+#include <stdexcept>
+#include <string.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "kms++.h"
+
+#ifndef DRM_CLIENT_CAP_ATOMIC
+#define DRM_CLIENT_CAP_ATOMIC 3
+#endif
+
+namespace kms
+{
+
+Card::Card()
+{
+       const char *card = "/dev/dri/card0";
+
+       int fd = open(card, O_RDWR | O_CLOEXEC);
+       if (fd < 0)
+               throw std::invalid_argument("foo");
+       m_fd = fd;
+
+       int r;
+
+       r = drmSetMaster(fd);
+       m_master = r == 0;
+
+       r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+       if (r)
+               throw std::invalid_argument("foo");
+
+       r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1);
+       m_has_atomic = r == 0;
+
+       uint64_t has_dumb;
+       r = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb);
+       if (r || !has_dumb)
+               throw std::invalid_argument("foo");
+
+       auto res = drmModeGetResources(m_fd);
+       if (!res)
+               throw std::invalid_argument("foo");
+
+       for (int i = 0; i < res->count_connectors; ++i) {
+               uint32_t id = res->connectors[i];
+               m_obmap[id] = new Connector(*this, id, i);
+       }
+
+       for (int i = 0; i < res->count_crtcs; ++i) {
+               uint32_t id = res->crtcs[i];
+               m_obmap[id] = new Crtc(*this, id, i);
+       }
+
+       for (int i = 0; i < res->count_encoders; ++i) {
+               uint32_t id = res->encoders[i];
+               m_obmap[id] = new Encoder(*this, id);
+       }
+
+       drmModeFreeResources(res);
+
+       auto planeRes = drmModeGetPlaneResources(m_fd);
+
+       for (uint i = 0; i < planeRes->count_planes; ++i) {
+               uint32_t id = planeRes->planes[i];
+               m_obmap[id] = new Plane(*this, id);
+       }
+
+       drmModeFreePlaneResources(planeRes);
+
+       // collect all possible props
+       for (auto ob : get_objects()) {
+               auto props = drmModeObjectGetProperties(m_fd, ob->id(), ob->object_type());
+
+               if (props == nullptr)
+                       continue;
+
+               for (unsigned i = 0; i < props->count_props; ++i) {
+                       uint32_t prop_id = props->props[i];
+
+                       if (m_obmap.find(prop_id) == m_obmap.end())
+                               m_obmap[prop_id] = new Property(*this, prop_id);
+               }
+
+               drmModeFreeObjectProperties(props);
+       }
+
+       for (auto pair : m_obmap)
+               pair.second->setup();
+}
+
+Card::~Card()
+{
+       for (auto pair : m_obmap)
+               delete pair.second;
+
+       close(m_fd);
+}
+
+template <class T> static void print_obs(const std::map<uint32_t, DrmObject*>& obmap)
+{
+       for (auto pair : obmap) {
+               auto ob = pair.second;
+               if (dynamic_cast<T*>(ob)) {
+                       ob->print_short();
+                       //ob->print_props();
+               }
+       }
+}
+
+void Card::print_short() const
+{
+       print_obs<Connector>(m_obmap);
+       print_obs<Encoder>(m_obmap);
+       print_obs<Crtc>(m_obmap);
+       print_obs<Plane>(m_obmap);
+}
+
+Property* Card::get_prop(const char *name) const
+{
+       for (auto pair : m_obmap) {
+               auto prop = dynamic_cast<Property*>(pair.second);
+               if (!prop)
+                       continue;
+
+               if (strcmp(name, prop->name()) == 0)
+                       return prop;
+       }
+
+       throw std::invalid_argument("foo");
+}
+
+Connector* Card::get_first_connected_connector() const
+{
+       for(auto pair : m_obmap) {
+               auto c = dynamic_cast<Connector*>(pair.second);
+
+               if (c && c->connected())
+                       return c;
+       }
+
+       throw std::invalid_argument("no connected connectors");
+}
+
+DrmObject* Card::get_object(uint32_t id) const
+{
+       return m_obmap.at(id);
+}
+
+std::vector<Connector*> Card::get_connectors() const
+{
+       std::vector<Connector*> v;
+       for(auto pair : m_obmap) {
+               auto p = dynamic_cast<Connector*>(pair.second);
+               if (p)
+                       v.push_back(p);
+       }
+       return v;
+}
+
+std::vector<Plane*> Card::get_planes() const
+{
+       std::vector<Plane*> v;
+       for(auto pair : m_obmap) {
+               auto p = dynamic_cast<Plane*>(pair.second);
+               if (p)
+                       v.push_back(p);
+       }
+       return v;
+}
+
+std::vector<DrmObject*> Card::get_objects() const
+{
+       std::vector<DrmObject*> v;
+       for(auto pair : m_obmap)
+               v.push_back(pair.second);
+       return v;
+}
+
+Crtc* Card::get_crtc_by_index(uint32_t idx) const
+{
+       for(auto pair : m_obmap) {
+               auto crtc = dynamic_cast<Crtc*>(pair.second);
+               if (crtc && crtc->idx() == idx)
+                       return crtc;
+       }
+       throw std::invalid_argument("fob");
+}
+
+Crtc* Card::get_crtc(uint32_t id) const { return dynamic_cast<Crtc*>(get_object(id)); }
+Encoder* Card::get_encoder(uint32_t id) const { return dynamic_cast<Encoder*>(get_object(id)); }
+Property* Card::get_prop(uint32_t id) const { return dynamic_cast<Property*>(get_object(id)); }
+}
diff --git a/libkms++/card.h b/libkms++/card.h
new file mode 100644 (file)
index 0000000..fb45d04
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <map>
+
+#include "decls.h"
+
+namespace kms
+{
+
+class Card
+{
+public:
+       Card();
+       ~Card();
+
+       Card(const Card& other) = delete;
+       Card& operator=(const Card& other) = delete;
+
+       int fd() const { return m_fd; }
+
+       Connector* get_first_connected_connector() const;
+
+       DrmObject* get_object(uint32_t id) const;
+       Crtc* get_crtc(uint32_t id) const;
+       Crtc* get_crtc_by_index(uint32_t idx) const;
+       Encoder* get_encoder(uint32_t id) const;
+       Property* get_prop(uint32_t id) const;
+       Property* get_prop(const char *name) const;
+
+       bool master() const { return m_master; }
+       bool has_atomic() const { return m_has_atomic; }
+
+       void print_short() const;
+
+       std::vector<Connector*> get_connectors() const;
+       std::vector<DrmObject*> get_objects() const;
+       std::vector<Plane*> get_planes() const;
+
+private:
+       std::map<uint32_t, DrmObject*> m_obmap;
+
+       int m_fd;
+       bool m_master;
+
+       bool m_has_atomic;
+};
+}
diff --git a/libkms++/connector.cpp b/libkms++/connector.cpp
new file mode 100644 (file)
index 0000000..ee14af9
--- /dev/null
@@ -0,0 +1,111 @@
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cassert>
+
+#include "kms++.h"
+#include "helpers.h"
+
+using namespace std;
+
+#define DEF_CONN(c) [DRM_MODE_CONNECTOR_##c] = #c
+
+namespace kms
+{
+
+static const char * connector_names[] = {
+       DEF_CONN(Unknown),
+       DEF_CONN(VGA),
+       DEF_CONN(DVII),
+       DEF_CONN(DVID),
+       DEF_CONN(DVIA),
+       DEF_CONN(Composite),
+       DEF_CONN(SVIDEO),
+       DEF_CONN(LVDS),
+       DEF_CONN(Component),
+       DEF_CONN(9PinDIN),
+       DEF_CONN(DisplayPort),
+       DEF_CONN(HDMIA),
+       DEF_CONN(HDMIB),
+       DEF_CONN(TV),
+       DEF_CONN(eDP),
+       DEF_CONN(VIRTUAL),
+       DEF_CONN(DSI),
+};
+
+static const char *connection_str[] = {
+       [0] = "<unknown>",
+       [DRM_MODE_CONNECTED] = "Connected",
+       [DRM_MODE_DISCONNECTED] = "Disconnected",
+       [DRM_MODE_UNKNOWNCONNECTION] = "Unknown",
+};
+
+struct ConnectorPriv
+{
+       drmModeConnectorPtr drm_connector;
+};
+
+Connector::Connector(Card &card, uint32_t id, uint32_t idx)
+       :DrmObject(card, id, DRM_MODE_OBJECT_CONNECTOR, idx)
+{
+       m_priv = new ConnectorPriv();
+
+       m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id());
+       assert(m_priv->drm_connector);
+
+       auto name = connector_names[m_priv->drm_connector->connector_type];
+       m_fullname = std::string(string(name) + std::to_string(m_priv->drm_connector->connector_type_id));
+}
+
+
+Connector::~Connector()
+{
+       drmModeFreeConnector(m_priv->drm_connector);
+       delete m_priv;
+}
+
+void Connector::setup()
+{
+       if (m_priv->drm_connector->encoder_id != 0) {
+               auto enc = card().get_encoder(m_priv->drm_connector->encoder_id);
+               if (enc)
+                       m_current_crtc = enc->get_crtc();
+       }
+}
+
+void Connector::print_short() const
+{
+       auto c = m_priv->drm_connector;
+
+       printf("Connector %d, %s, %dx%dmm, %s\n", id(), m_fullname.c_str(),
+              c->mmWidth, c->mmHeight, connection_str[c->connection]);
+}
+
+Videomode Connector::get_default_mode() const
+{
+       drmModeModeInfo drmmode = m_priv->drm_connector->modes[0];
+
+       return drm_mode_to_video_mode(drmmode);
+}
+
+bool Connector::connected()
+{
+       return m_priv->drm_connector->connection == DRM_MODE_CONNECTED;
+}
+
+vector<Crtc*> Connector::get_possible_crtcs() const
+{
+       vector<Crtc*> crtcs;
+
+       for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) {
+               auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]);
+
+               auto l = enc->get_possible_crtcs();
+
+               crtcs.insert(crtcs.end(), l.begin(), l.end());
+       }
+
+       return crtcs;
+}
+}
diff --git a/libkms++/connector.h b/libkms++/connector.h
new file mode 100644 (file)
index 0000000..b7d975c
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <vector>
+
+#include "drmobject.h"
+
+namespace kms
+{
+
+struct ConnectorPriv;
+
+struct Videomode
+{
+       uint32_t clock;
+       uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
+       uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
+
+       uint32_t vrefresh;
+
+       uint32_t flags;
+       uint32_t type;
+       char name[32]; // XXX
+};
+
+class Connector : public DrmObject
+{
+public:
+       Connector(Card& card, uint32_t id, uint32_t idx);
+       ~Connector();
+
+       void setup();
+
+       void print_short() const;
+
+       Videomode get_default_mode() const;
+
+       Crtc* get_current_crtc() const { return m_current_crtc; }
+       std::vector<Crtc*> get_possible_crtcs() const;
+
+       bool connected();
+
+private:
+       ConnectorPriv* m_priv;
+
+       std::string m_fullname;
+
+       Crtc* m_current_crtc;
+};
+}
diff --git a/libkms++/crtc.cpp b/libkms++/crtc.cpp
new file mode 100644 (file)
index 0000000..e583be8
--- /dev/null
@@ -0,0 +1,74 @@
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cassert>
+
+#include "kms++.h"
+#include "helpers.h"
+
+using namespace std;
+
+namespace kms
+{
+
+struct CrtcPriv
+{
+       drmModeCrtcPtr drm_crtc;
+};
+
+Crtc::Crtc(Card &card, uint32_t id, uint32_t idx)
+       :DrmObject(card, id, DRM_MODE_OBJECT_CRTC, idx)
+{
+       m_priv = new CrtcPriv();
+       m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id());
+       assert(m_priv->drm_crtc);
+}
+
+Crtc::~Crtc()
+{
+       drmModeFreeCrtc(m_priv->drm_crtc);
+       delete m_priv;
+}
+
+void Crtc::setup()
+{
+       for (Plane* plane : card().get_planes()) {
+               if (plane->supports_crtc(this))
+                       m_possible_planes.push_back(plane);
+       }
+}
+
+void Crtc::print_short() const
+{
+       auto c  = m_priv->drm_crtc;
+
+       printf("Crtc %d, %d,%d %dx%d\n", id(),
+              c->x, c->y, c->width, c->height);
+}
+
+int Crtc::set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode)
+{
+       uint32_t conns[] = { conn->id() };
+       drmModeModeInfo drmmode = video_mode_to_drm_mode(mode);
+
+       return drmModeSetCrtc(card().fd(), id(), fb.id(),
+                             0, 0,
+                             conns, 1, &drmmode);
+}
+
+static inline uint32_t conv(float x)
+{
+       // XXX fix the conversion for fractional part
+       return ((uint32_t)x) << 16;
+}
+
+int Crtc::set_plane(Plane* plane, Framebuffer& fb,
+                   int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h,
+                   float src_x, float src_y, float src_w, float src_h)
+{
+       return drmModeSetPlane(card().fd(), plane->id(), id(), fb.id(), 0,
+                              dst_x, dst_y, dst_w, dst_h,
+                              conv(src_x), conv(src_y), conv(src_w), conv(src_h));
+}
+}
diff --git a/libkms++/crtc.h b/libkms++/crtc.h
new file mode 100644 (file)
index 0000000..ac05da9
--- /dev/null
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <vector>
+
+#include "drmobject.h"
+
+namespace kms
+{
+
+struct CrtcPriv;
+
+class Crtc : public DrmObject
+{
+public:
+       Crtc(Card& card, uint32_t id, uint32_t idx);
+       ~Crtc();
+
+       void setup();
+
+       void print_short() const;
+
+       const std::vector<Plane*>& get_possible_planes() const { return m_possible_planes; }
+
+       int set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode);
+
+       int set_plane(Plane *plane, Framebuffer &fb,
+                     int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h,
+                     float src_x, float src_y, float src_w, float src_h);
+
+private:
+       CrtcPriv* m_priv;
+
+       std::vector<Plane*> m_possible_planes;
+};
+}
diff --git a/libkms++/decls.h b/libkms++/decls.h
new file mode 100644 (file)
index 0000000..c2cf09f
--- /dev/null
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace kms
+{
+class AtomicReq;
+class Card;
+class Connector;
+class Crtc;
+class Encoder;
+class Framebuffer;
+class DrmObject;
+class Plane;
+class Property;
+class Videomode;
+}
diff --git a/libkms++/drmobject.cpp b/libkms++/drmobject.cpp
new file mode 100644 (file)
index 0000000..ea5d594
--- /dev/null
@@ -0,0 +1,69 @@
+#include <string.h>
+#include <iostream>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "kms++.h"
+
+using namespace std;
+
+namespace kms
+{
+
+DrmObject::DrmObject(Card& card, uint32_t object_type)
+       :m_id(-1), m_card(card), m_object_type(object_type)
+{
+}
+
+DrmObject::DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx)
+       :m_id(id), m_card(card), m_object_type(object_type), m_idx(idx)
+{
+       refresh_props();
+}
+
+DrmObject::~DrmObject()
+{
+
+}
+
+void DrmObject::refresh_props()
+{
+       auto props = drmModeObjectGetProperties(card().fd(), this->id(), this->object_type());
+
+       if (props == nullptr)
+               return;
+
+       for (unsigned i = 0; i < props->count_props; ++i) {
+               uint32_t prop_id = props->props[i];
+               uint64_t prop_value = props->prop_values[i];
+
+               m_prop_values[prop_id] = prop_value;
+       }
+
+       drmModeFreeObjectProperties(props);
+}
+
+void DrmObject::print_props() const
+{
+       for (auto it = m_prop_values.begin(); it != m_prop_values.end(); ++it) {
+               cout << "\t" << card().get_prop(it->first)->name() <<
+                       " = " << it->second << endl;
+       }
+}
+
+uint64_t DrmObject::get_prop_value(uint32_t id) const
+{
+       return m_prop_values.at(id);
+}
+
+uint64_t DrmObject::get_prop_value(const char *name) const
+{
+       for (auto pair : m_prop_values) {
+               auto prop = card().get_prop(pair.first);
+               if (strcmp(name, prop->name()) == 0)
+                       return m_prop_values.at(prop->id());
+       }
+
+       throw invalid_argument("foo");
+}
+}
diff --git a/libkms++/drmobject.h b/libkms++/drmobject.h
new file mode 100644 (file)
index 0000000..5c945f9
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <map>
+
+#include "decls.h"
+
+namespace kms
+{
+
+class DrmObject
+{
+public:
+       DrmObject(Card& card, uint32_t object_type);
+       DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx = 0);
+       virtual ~DrmObject();
+
+       DrmObject(const DrmObject& other) = delete;
+       DrmObject& operator=(const DrmObject& other) = delete;
+
+       virtual void setup() { };
+
+       virtual void print_short() const = 0;
+       void print_props() const;
+
+       uint32_t id() const { return m_id; }
+       Card& card() const { return m_card; }
+
+       uint32_t object_type() const { return m_object_type; }
+       uint32_t idx() const { return m_idx; }
+
+       void refresh_props();
+       uint64_t get_prop_value(uint32_t id) const;
+       uint64_t get_prop_value(const char *name) const;
+
+protected:
+       uint32_t m_id;  // protected for Framebuffer...
+
+private:
+       Card& m_card;
+       std::map<uint32_t, uint64_t> m_prop_values;
+       uint32_t m_object_type;
+       uint32_t m_idx;
+};
+}
diff --git a/libkms++/encoder.cpp b/libkms++/encoder.cpp
new file mode 100644 (file)
index 0000000..081177b
--- /dev/null
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cassert>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "kms++.h"
+
+using namespace std;
+
+namespace kms
+{
+
+struct EncoderPriv
+{
+       drmModeEncoderPtr drm_encoder;
+};
+
+Encoder::Encoder(Card &card, uint32_t id)
+       :DrmObject(card, id, DRM_MODE_OBJECT_ENCODER)
+{
+       m_priv = new EncoderPriv();
+       m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id());
+       assert(m_priv->drm_encoder);
+}
+
+Encoder::~Encoder()
+{
+       drmModeFreeEncoder(m_priv->drm_encoder);
+       delete m_priv;
+}
+
+void Encoder::print_short() const
+{
+       auto e = m_priv->drm_encoder;
+
+       printf("Encoder %d, %d\n", id(),
+              e->encoder_type);
+}
+
+Crtc* Encoder::get_crtc() const
+{
+       return card().get_crtc(m_priv->drm_encoder->crtc_id);
+}
+
+vector<Crtc*> Encoder::get_possible_crtcs() const
+{
+       unsigned bits = m_priv->drm_encoder->possible_crtcs;
+       vector<Crtc*> crtcs;
+
+       for (int idx = 0; bits; idx++, bits >>= 1) {
+               if ((bits & 1) == 0)
+                       continue;
+
+               auto crtc = card().get_crtc_by_index(idx);
+               crtcs.push_back(crtc);
+       }
+
+       return crtcs;
+}
+}
diff --git a/libkms++/encoder.h b/libkms++/encoder.h
new file mode 100644 (file)
index 0000000..3e9a8e4
--- /dev/null
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <vector>
+#include "drmobject.h"
+
+namespace kms
+{
+
+struct EncoderPriv;
+
+class Encoder : public DrmObject
+{
+public:
+       Encoder(Card& card, uint32_t id);
+       ~Encoder();
+
+       void print_short() const;
+
+       Crtc* get_crtc() const;
+       std::vector<Crtc*> get_possible_crtcs() const;
+
+private:
+       EncoderPriv* m_priv;
+};
+}
diff --git a/libkms++/framebuffer.cpp b/libkms++/framebuffer.cpp
new file mode 100644 (file)
index 0000000..5382a6f
--- /dev/null
@@ -0,0 +1,167 @@
+
+#include <cstring>
+#include <stdexcept>
+#include <sys/mman.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+#include <drm.h>
+#include <drm_mode.h>
+
+#include "kms++.h"
+#include "utils/testpat.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+namespace kms
+{
+
+Framebuffer::Framebuffer(Card &card, uint32_t width, uint32_t height, const char* fourcc)
+       :DrmObject(card, DRM_MODE_OBJECT_FB)
+{
+       uint32_t a, b, c, d;
+       a = fourcc[0];
+       b = fourcc[1];
+       c = fourcc[2];
+       d = fourcc[3];
+
+       uint32_t code = ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24));
+
+       Create(width, height, code);
+}
+
+Framebuffer::~Framebuffer()
+{
+       Destroy();
+}
+
+void Framebuffer::print_short() const
+{
+       printf("Framebuffer %d\n", id());
+}
+
+struct FormatPlaneInfo
+{
+       uint8_t bitspp; /* bits per (macro) pixel */
+       uint8_t xsub;
+       uint8_t ysub;
+};
+
+struct FormatInfo
+{
+       uint32_t format;
+       uint8_t num_planes;
+       struct FormatPlaneInfo planes[4];
+};
+
+static const FormatInfo format_info_array[] = {
+       /* YUV packed */
+       { DRM_FORMAT_UYVY, 1, { { 32, 2, 1 } }, },
+       { DRM_FORMAT_YUYV, 1, { { 32, 2, 1 } }, },
+       /* YUV semi-planar */
+       { DRM_FORMAT_NV12, 2, { { 8, 1, 1, }, { 16, 2, 2 } }, },
+       /* RGB16 */
+       { DRM_FORMAT_RGB565, 1, { { 16, 1, 1 } }, },
+       /* RGB32 */
+       { DRM_FORMAT_XRGB8888, 1, { { 32, 1, 1 } }, },
+};
+
+static const FormatInfo& find_format(uint32_t format)
+{
+       for (uint i = 0; i < ARRAY_SIZE(format_info_array); ++i) {
+               if (format == format_info_array[i].format)
+                       return format_info_array[i];
+       }
+
+       throw std::invalid_argument("foo");
+}
+
+void Framebuffer::Create(uint32_t width, uint32_t height, uint32_t format)
+{
+       int r;
+
+       m_width = width;
+       m_height = height;
+       m_format = format;
+
+       const FormatInfo& format_info = find_format(format);
+
+       m_num_planes = format_info.num_planes;
+
+       for (int i = 0; i < format_info.num_planes; ++i) {
+               const FormatPlaneInfo& pi = format_info.planes[i];
+               FramebufferPlane& plane = m_planes[i];
+
+               /* create dumb buffer */
+               struct drm_mode_create_dumb creq = drm_mode_create_dumb();
+               creq.width = m_width / pi.xsub;
+               creq.height = m_height / pi.ysub;
+               creq.bpp = pi.bitspp;
+               r = drmIoctl(card().fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+               if (r)
+                       throw std::invalid_argument("foo");
+
+               plane.handle = creq.handle;
+               plane.stride = creq.pitch;
+               plane.size = creq.height * creq.pitch;
+
+               /*
+               printf("buf %d: %dx%d, bitspp %d, stride %d, size %d\n",
+                       i, creq.width, creq.height, pi->bitspp, plane->stride, plane->size);
+               */
+
+               /* prepare buffer for memory mapping */
+               struct drm_mode_map_dumb mreq = drm_mode_map_dumb();
+               mreq.handle = plane.handle;
+               r = drmIoctl(card().fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+               if (r)
+                       throw std::invalid_argument("foo");
+
+               /* perform actual memory mapping */
+               m_planes[i].map = (uint8_t *)mmap(0, plane.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                                                 card().fd(), mreq.offset);
+               if (plane.map == MAP_FAILED)
+                       throw std::invalid_argument("foo");
+
+               /* clear the framebuffer to 0 */
+               memset(plane.map, 0, plane.size);
+       }
+
+       /* create framebuffer object for the dumb-buffer */
+       uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle };
+       uint32_t pitches[4] = { m_planes[0].stride, m_planes[1].stride };
+       uint32_t offsets[4] = { 0 };
+       uint32_t id;
+       r = drmModeAddFB2(card().fd(), m_width, m_height, format,
+                         bo_handles, pitches, offsets, &id, 0);
+       if (r)
+               throw std::invalid_argument("foo");
+
+       m_id = id;
+}
+
+void Framebuffer::Destroy()
+{
+       /* delete framebuffer */
+       drmModeRmFB(card().fd(), id());
+
+       for (uint i = 0; i < m_num_planes; ++i) {
+               FramebufferPlane& plane = m_planes[i];
+
+               /* unmap buffer */
+               munmap(plane.map, plane.size);
+
+               /* delete dumb buffer */
+               struct drm_mode_destroy_dumb dreq = drm_mode_destroy_dumb();
+               dreq.handle = plane.handle;
+               drmIoctl(card().fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+
+       }
+}
+
+void Framebuffer::clear()
+{
+       for (unsigned i = 0; i < m_num_planes; ++i)
+               memset(m_planes[i].map, 0, m_planes[i].size);
+}
+}
diff --git a/libkms++/framebuffer.h b/libkms++/framebuffer.h
new file mode 100644 (file)
index 0000000..2710528
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "drmobject.h"
+
+namespace kms
+{
+
+class Framebuffer : public DrmObject
+{
+public:
+       Framebuffer(Card& card, uint32_t width, uint32_t height, const char* fourcc);
+       virtual ~Framebuffer();
+
+       void print_short() const;
+
+       uint32_t width() const { return m_width; }
+       uint32_t height() const { return m_height; }
+       uint32_t format() const { return m_format; }
+
+       uint8_t* map(unsigned plane) const { return m_planes[plane].map; }
+       uint32_t stride(unsigned plane) const { return m_planes[plane].stride; }
+       uint32_t size(unsigned plane) const { return m_planes[plane].size; }
+
+       void clear();
+
+private:
+       struct FramebufferPlane {
+               uint32_t handle;
+               uint32_t size;
+               uint32_t stride;
+               uint8_t *map;
+       };
+
+       void Create(uint32_t width, uint32_t height, uint32_t format);
+       void Destroy();
+
+       unsigned m_num_planes;
+       struct FramebufferPlane m_planes[4];
+
+       uint32_t m_width;
+       uint32_t m_height;
+       uint32_t m_format;
+};
+}
diff --git a/libkms++/helpers.cpp b/libkms++/helpers.cpp
new file mode 100644 (file)
index 0000000..7746bde
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "connector.h"
+#include "helpers.h"
+#include <cstring>
+
+namespace kms
+{
+Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode)
+{
+       // XXX these are the same at the moment
+       Videomode mode;
+       memcpy(&mode, &drmmode, sizeof(mode));
+       return mode;
+}
+
+drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode)
+{
+       // XXX these are the same at the moment
+       drmModeModeInfo drmmode;
+       memcpy(&drmmode, &mode, sizeof(drmmode));
+       return drmmode;
+}
+}
diff --git a/libkms++/helpers.h b/libkms++/helpers.h
new file mode 100644 (file)
index 0000000..ac640e8
--- /dev/null
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+namespace kms
+{
+class Videomode;
+
+Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode);
+drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode);
+}
diff --git a/libkms++/kms++.h b/libkms++/kms++.h
new file mode 100644 (file)
index 0000000..f9808ed
--- /dev/null
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "atomicreq.h"
+#include "card.h"
+#include "connector.h"
+#include "crtc.h"
+#include "encoder.h"
+#include "framebuffer.h"
+#include "plane.h"
+#include "property.h"
diff --git a/libkms++/plane.cpp b/libkms++/plane.cpp
new file mode 100644 (file)
index 0000000..afb9c78
--- /dev/null
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cassert>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "kms++.h"
+
+using namespace std;
+
+namespace kms
+{
+
+struct PlanePriv
+{
+       drmModePlanePtr drm_plane;
+};
+
+Plane::Plane(Card &card, uint32_t id)
+       :DrmObject(card, id, DRM_MODE_OBJECT_PLANE)
+{
+       m_priv = new PlanePriv();
+       m_priv->drm_plane = drmModeGetPlane(this->card().fd(), this->id());
+       assert(m_priv->drm_plane);
+}
+
+Plane::~Plane()
+{
+       drmModeFreePlane(m_priv->drm_plane);
+       delete m_priv;
+}
+
+void Plane::print_short() const
+{
+       auto p = m_priv->drm_plane;
+
+       printf("Plane %d, %d modes, %d,%d -> %dx%d\n", id(),
+              p->count_formats,
+              p->crtc_x, p->crtc_y, p->x, p->y);
+
+       printf("\t");
+       for (unsigned i = 0; i < p->count_formats; ++i) {
+               uint32_t f = p->formats[i];
+               printf("%c%c%c%c ",
+                      (f >> 0) & 0xff,
+                      (f >> 8) & 0xff,
+                      (f >> 16) & 0xff,
+                      (f >> 24) & 0xff);
+       }
+       printf("\n");
+}
+
+bool Plane::supports_crtc(Crtc* crtc) const
+{
+       return m_priv->drm_plane->possible_crtcs & (1 << crtc->idx());
+}
+
+PlaneType Plane::plane_type() const
+{
+       return (PlaneType)get_prop_value("type");
+}
+}
diff --git a/libkms++/plane.h b/libkms++/plane.h
new file mode 100644 (file)
index 0000000..890a28f
--- /dev/null
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "drmobject.h"
+
+namespace kms
+{
+
+enum class PlaneType
+{
+       Overlay = 0,
+       Primary = 1,
+       Cursor = 2,
+};
+
+struct PlanePriv;
+
+class Plane : public DrmObject
+{
+public:
+       Plane(Card& card, uint32_t id);
+       ~Plane();
+
+       void print_short() const;
+
+       bool supports_crtc(Crtc* crtc) const;
+
+       PlaneType plane_type() const;
+
+private:
+       PlanePriv* m_priv;
+};
+}
diff --git a/libkms++/property.cpp b/libkms++/property.cpp
new file mode 100644 (file)
index 0000000..af6a0ae
--- /dev/null
@@ -0,0 +1,36 @@
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "kms++.h"
+
+namespace kms
+{
+
+struct PropertyPriv
+{
+       drmModePropertyPtr drm_prop;
+};
+
+Property::Property(Card& card, uint32_t id)
+       : DrmObject(card, id, DRM_MODE_OBJECT_PROPERTY)
+{
+       m_priv = new PropertyPriv();
+       m_priv->drm_prop = drmModeGetProperty(card.fd(), id);
+}
+
+Property::~Property()
+{
+       drmModeFreeProperty(m_priv->drm_prop);
+       delete m_priv;
+}
+
+void Property::print_short() const
+{
+       printf("Property %d, %s\n", id(), name());
+}
+
+const char *Property::name() const
+{
+       return m_priv->drm_prop->name;
+}
+}
diff --git a/libkms++/property.h b/libkms++/property.h
new file mode 100644 (file)
index 0000000..d5306d0
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "drmobject.h"
+
+namespace kms
+{
+
+struct PropertyPriv;
+
+class Property : public DrmObject
+{
+public:
+       Property(Card& card, uint32_t id);
+       ~Property();
+
+       void print_short() const;
+
+       const char *name() const;
+
+private:
+       PropertyPriv* m_priv;
+};
+}
diff --git a/libkms++/utils/color.cpp b/libkms++/utils/color.cpp
new file mode 100644 (file)
index 0000000..b5b9001
--- /dev/null
@@ -0,0 +1,67 @@
+#include "color.h"
+
+namespace kms
+{
+RGB::RGB()
+{
+       r = g = b = a = 0;
+}
+
+RGB::RGB(uint8_t r, uint8_t g, uint8_t b)
+{
+       this->r = r;
+       this->g = g;
+       this->b = b;
+       this->a = 0;
+}
+
+uint16_t RGB::rgb565() const
+{
+       uint16_t r = (this->r >> 3) << 11;
+       uint16_t g = (this->g >> 2) << 5;
+       uint16_t b = (this->b >> 3) << 0;
+       return r | g | b;
+}
+
+YUV RGB::yuv() const
+{
+       return YUV(*this);
+}
+
+
+YUV::YUV()
+{
+       y = u = v = a = 0;
+}
+
+YUV::YUV(uint8_t y, uint8_t u, uint8_t v)
+{
+       this->y = y;
+       this->u = u;
+       this->v = v;
+       this->a = 0;
+}
+
+static inline uint8_t MAKE_YUV_601_Y(uint8_t r, uint8_t g, uint8_t b)
+{
+       return (((66 * r + 129 * g +  25 * b + 128) >> 8) + 16);
+}
+
+static inline uint8_t MAKE_YUV_601_U(uint8_t r, uint8_t g, uint8_t b)
+{
+       return (((-38 * r -  74 * g + 112 * b + 128) >> 8) + 128);
+}
+
+static inline uint8_t MAKE_YUV_601_V(uint8_t r, uint8_t g, uint8_t b)
+{
+       return (((112 * r -  94 * g -  18 * b + 128) >> 8) + 128);
+}
+
+YUV::YUV(const RGB& rgb)
+{
+       this->y = MAKE_YUV_601_Y(rgb.r, rgb.g, rgb.b);
+       this->u = MAKE_YUV_601_U(rgb.r, rgb.g, rgb.b);
+       this->v = MAKE_YUV_601_V(rgb.r, rgb.g, rgb.b);
+       this->a = rgb.a;
+}
+}
diff --git a/libkms++/utils/color.h b/libkms++/utils/color.h
new file mode 100644 (file)
index 0000000..1db47e8
--- /dev/null
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <cstdint>
+
+namespace kms
+{
+struct YUV;
+
+struct RGB
+{
+       RGB();
+       RGB(uint8_t r, uint8_t g, uint8_t b);
+
+       uint16_t rgb565() const;
+       YUV yuv() const;
+
+       union {
+               struct
+               {
+                       uint8_t b;
+                       uint8_t g;
+                       uint8_t r;
+                       uint8_t a;
+               };
+
+               uint32_t raw;
+       };
+};
+
+struct YUV
+{
+       YUV();
+       YUV(uint8_t y, uint8_t u, uint8_t v);
+       YUV(const RGB& rgb);
+
+       union {
+               struct
+               {
+                       uint8_t v;
+                       uint8_t u;
+                       uint8_t y;
+                       uint8_t a;
+               };
+
+               uint32_t raw;
+       };
+};
+}
diff --git a/libkms++/utils/conv.cpp b/libkms++/utils/conv.cpp
new file mode 100644 (file)
index 0000000..d439253
--- /dev/null
@@ -0,0 +1,138 @@
+#include <drm_fourcc.h>
+#include <stdexcept>
+
+#include "framebuffer.h"
+#include "color.h"
+#include "conv.h"
+
+namespace kms
+{
+static RGB read_rgb(const Framebuffer& fb, int x, int y)
+{
+       uint32_t *pc = (uint32_t *)(fb.map(0) + fb.stride(0) * y);
+
+       uint32_t c = pc[x];
+
+       return RGB((c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
+}
+
+static YUV read_rgb_as_yuv(const Framebuffer& fb, int x, int y)
+{
+       RGB rgb = read_rgb(fb, x, y);
+       return YUV(rgb);
+}
+
+static void fb_rgb_to_packed_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb)
+{
+       unsigned w = src_fb.width();
+       unsigned h = src_fb.height();
+
+       uint8_t *dst = dst_fb.map(0);
+
+       for (unsigned y = 0; y < h; ++y) {
+               for (unsigned x = 0; x < w; x += 2) {
+                       YUV yuv1 = read_rgb_as_yuv(src_fb, x + 0, y);
+                       YUV yuv2 = read_rgb_as_yuv(src_fb, x + 1, y);
+
+                       switch (dst_fb.format()) {
+                       case DRM_FORMAT_UYVY:
+                               dst[x * 2 + 0] = (yuv1.u + yuv2.u) / 2;
+                               dst[x * 2 + 1] = yuv1.y;
+                               dst[x * 2 + 2] = (yuv1.v + yuv2.v) / 2;
+                               dst[x * 2 + 3] = yuv2.y;
+                               break;
+                       case DRM_FORMAT_YUYV:
+                               dst[x * 2 + 0] = yuv1.y;
+                               dst[x * 2 + 1] = (yuv1.u + yuv2.u) / 2;
+                               dst[x * 2 + 2] = yuv2.y;
+                               dst[x * 2 + 3] = (yuv1.v + yuv2.v) / 2;
+                               break;
+
+                       default:
+                               throw std::invalid_argument("fo");
+                       }
+               }
+
+               dst += dst_fb.stride(0);
+       }
+}
+
+static void fb_rgb_to_semiplanar_yuv(Framebuffer& dst_fb, const Framebuffer& src_fb)
+{
+       unsigned w = src_fb.width();
+       unsigned h = src_fb.height();
+
+       uint8_t *dst_y = dst_fb.map(0);
+       uint8_t *dst_uv = dst_fb.map(1);
+
+       for (unsigned y = 0; y < h; ++y) {
+               for (unsigned x = 0; x < w; ++x) {
+                       YUV yuv = read_rgb_as_yuv(src_fb, x, y);
+                       dst_y[x] = yuv.y;
+               }
+
+               dst_y += dst_fb.stride(0);
+       }
+
+       for (unsigned y = 0; y < h; y += 2) {
+               for (unsigned x = 0; x < w; x += 2) {
+                       YUV yuv00 = read_rgb_as_yuv(src_fb, x + 0, y + 0);
+                       YUV yuv01 = read_rgb_as_yuv(src_fb, x + 1, y + 0);
+                       YUV yuv10 = read_rgb_as_yuv(src_fb, x + 0, y + 1);
+                       YUV yuv11 = read_rgb_as_yuv(src_fb, x + 1, y + 1);
+
+                       unsigned u = (yuv00.u + yuv01.u + yuv10.u + yuv11.u) / 4;
+                       unsigned v = (yuv00.v + yuv01.v + yuv10.v + yuv11.v) / 4;
+
+                       dst_uv[x + 0] = u;
+                       dst_uv[x + 1] = v;
+               }
+
+               dst_uv += dst_fb.stride(1);
+       }
+}
+
+static void fb_rgb_to_rgb565(Framebuffer& dst_fb, const Framebuffer& src_fb)
+{
+       unsigned w = src_fb.width();
+       unsigned h = src_fb.height();
+
+       uint8_t *dst = dst_fb.map(0);
+
+       for (unsigned y = 0; y < h; ++y) {
+               for (unsigned x = 0; x < w; ++x) {
+                       RGB rgb = read_rgb(src_fb, x, y);
+
+                       unsigned r = rgb.r * 32 / 256;
+                       unsigned g = rgb.g * 64 / 256;
+                       unsigned b = rgb.b * 32 / 256;
+
+                       ((uint16_t *)dst)[x] = (r << 11) | (g << 5) | (b << 0);
+               }
+
+               dst += dst_fb.stride(0);
+       }
+}
+
+void color_convert(Framebuffer& dst, const Framebuffer &src)
+{
+       switch (dst.format()) {
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+               fb_rgb_to_semiplanar_yuv(dst, src);
+               break;
+
+       case DRM_FORMAT_YUYV:
+       case DRM_FORMAT_UYVY:
+               fb_rgb_to_packed_yuv(dst, src);
+               break;
+
+       case DRM_FORMAT_RGB565:
+               fb_rgb_to_rgb565(dst, src);
+               break;
+
+       default:
+               throw std::invalid_argument("fo");
+       }
+}
+}
diff --git a/libkms++/utils/conv.h b/libkms++/utils/conv.h
new file mode 100644 (file)
index 0000000..d1b306a
--- /dev/null
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace kms
+{
+class Framebuffer;
+
+void color_convert(Framebuffer& dst, const Framebuffer &src);
+}
diff --git a/libkms++/utils/testpat.cpp b/libkms++/utils/testpat.cpp
new file mode 100644 (file)
index 0000000..cd35e0e
--- /dev/null
@@ -0,0 +1,162 @@
+
+#include <chrono>
+#include <cstring>
+#include <cassert>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+#include <drm.h>
+#include <drm_mode.h>
+
+#include "card.h"
+#include "framebuffer.h"
+#include "testpat.h"
+#include "color.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+namespace kms
+{
+static void draw_pixel(Framebuffer& buf, unsigned x, unsigned y, RGB color)
+{
+       static RGB c1;
+
+       switch (buf.format()) {
+       case DRM_FORMAT_XRGB8888:
+       {
+               uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
+               *p = color.raw;
+               break;
+       }
+       case DRM_FORMAT_RGB565:
+       {
+               uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
+               *p = color.rgb565();
+               break;
+       }
+       case DRM_FORMAT_UYVY:
+       {
+               if ((x & 1) == 0) {
+                       c1 = color;
+                       return;
+               }
+
+               uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
+
+               YUV yuv1 = c1.yuv();
+               YUV yuv2 = color.yuv();
+
+               p[0] = (yuv1.u + yuv2.u) / 2;
+               p[1] = yuv1.y;
+               p[2] = (yuv1.v + yuv2.v) / 2;
+               p[3] = yuv2.y;
+               break;
+       }
+       case DRM_FORMAT_YUYV:
+       {
+               if ((x & 1) == 0) {
+                       c1 = color;
+                       return;
+               }
+
+               uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
+
+               YUV yuv1 = c1.yuv();
+               YUV yuv2 = color.yuv();
+
+               p[0] = yuv1.y;
+               p[1] = (yuv1.u + yuv2.u) / 2;
+               p[2] = yuv2.y;
+               p[3] = (yuv1.v + yuv2.v) / 2;
+               break;
+       }
+       }
+}
+
+static void draw_rgb_test_pattern(Framebuffer& fb)
+{
+       unsigned x, y;
+       unsigned w = fb.width();
+       unsigned h = fb.height();
+
+       const unsigned mw = 20;
+
+       const unsigned xm1 = mw;
+       const unsigned xm2 = w - mw - 1;
+       const unsigned ym1 = mw;
+       const unsigned ym2 = h - mw - 1;
+
+       for (y = 0; y < h; y++) {
+               for (x = 0; x < w; x++) {
+                       // white margin lines
+                       if (x == xm1 || x == xm2 || y == ym1 || y == ym2)
+                               draw_pixel(fb, x, y, RGB(255, 255, 255));
+                       // white box outlines to corners
+                       else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2))
+                               draw_pixel(fb, x, y, RGB(255, 255, 255));
+                       // white box outlines to corners
+                       else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2))
+                               draw_pixel(fb, x, y, RGB(255, 255, 255));
+                       // blue bar on the left
+                       else if (x < xm1 && (y > ym1 && y < ym2))
+                               draw_pixel(fb, x, y, RGB(0, 0, 255));
+                       // blue bar on the top
+                       else if (y < ym1 && (x > xm1 && x < xm2))
+                               draw_pixel(fb, x, y, RGB(0, 0, 255));
+                       // red bar on the right
+                       else if (x > xm2 && (y > ym1 && y < ym2))
+                               draw_pixel(fb, x, y, RGB(255, 0, 0));
+                       // red bar on the bottom
+                       else if (y > ym2 && (x > xm1 && x < xm2))
+                               draw_pixel(fb, x, y, RGB(255, 0, 0));
+                       // inside the margins
+                       else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) {
+                               // diagonal line
+                               if (x == y || w - x == h - y)
+                                       draw_pixel(fb, x, y, RGB(255, 255, 255));
+                               // diagonal line
+                               else if (w - x == y || x == h - y)
+                                       draw_pixel(fb, x, y, RGB(255, 255, 255));
+                               else {
+                                       int t = (x - xm1 - 1) * 3 / (xm2 - xm1 - 1);
+                                       unsigned r = 0, g = 0, b = 0;
+
+                                       unsigned c = (y - ym1 - 1) % 256;
+
+                                       switch (t) {
+                                       case 0:
+                                               r = c;
+                                               break;
+                                       case 1:
+                                               g = c;
+                                               break;
+                                       case 2:
+                                               b = c;
+                                               break;
+                                       }
+
+                                       draw_pixel(fb, x, y, RGB(r, g, b));
+                               }
+                               // black corners
+                       } else {
+                               draw_pixel(fb, x, y, RGB(0, 0, 0));
+                       }
+               }
+       }
+}
+
+void draw_test_pattern(Framebuffer& fb)
+{
+       using namespace std::chrono;
+
+       auto t1 = high_resolution_clock::now();
+
+       draw_rgb_test_pattern(fb);
+
+       auto t2 = high_resolution_clock::now();
+       auto time_span = duration_cast<microseconds>(t2 - t1);
+
+       printf("draw took %lu us\n", time_span.count());
+}
+}
diff --git a/libkms++/utils/testpat.h b/libkms++/utils/testpat.h
new file mode 100644 (file)
index 0000000..b60271a
--- /dev/null
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace kms
+{
+void draw_test_pattern(Framebuffer& fb);
+}
diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ba70561
--- /dev/null
@@ -0,0 +1,20 @@
+set(SWIG_EXECUTABLE "swig3.0")
+find_package(SWIG 3.0 REQUIRED)
+include(${SWIG_USE_FILE})
+
+include_directories(${LIBDRM_INCLUDE_DIRS})
+link_directories(${LIBDRM_LIBRARY_DIRS})
+
+pkg_search_module(LUA REQUIRED lua5.2 lua)
+include_directories(${LUA_INCLUDE_DIRS})
+link_directories(${LUA_LIBRARY_DIRS})
+
+#include_directories(libkms)
+#XXX
+include_directories(../libkms++)
+
+set_source_files_properties(luakms.i PROPERTIES CPLUSPLUS ON)
+swig_add_module(luakms lua luakms.i)
+swig_link_libraries(luakms kms++ ${LIBDRM_LIBRARIES} ${LUA_LIBRARIES})
+
+add_custom_target(luaextras SOURCES test.lua)
diff --git a/lua/luakms.i b/lua/luakms.i
new file mode 100644 (file)
index 0000000..60931f1
--- /dev/null
@@ -0,0 +1,20 @@
+%module libluakms
+%{
+#include "kms++.h"
+#include "utils/testpat.h"
+using namespace kms;
+%}
+
+%include "stdint.i"
+
+%include "decls.h"
+%include "drmobject.h"
+%include "atomicreq.h"
+%include "crtc.h"
+%include "card.h"
+%include "property.h"
+%include "framebuffer.h"
+%include "plane.h"
+%include "connector.h"
+%include "encoder.h"
+%include "utils/testpat.h"
diff --git a/lua/test.lua b/lua/test.lua
new file mode 100755 (executable)
index 0000000..bc6c4fc
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/lua
+
+require("libluakms")
+require("bit32")
+
+card = libluakms.Card()
+card:print_short()
+
+conn = card:get_first_connected_connector()
+
+mode = conn:get_default_mode()
+
+fb = libluakms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+libluakms.draw_test_pattern(fb);
+
+crtc = conn:get_current_crtc()
+
+crtc:set_mode(conn, fb, mode)
+
+print("Press enter to exit")
+io.read()
diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt
new file mode 100644 (file)
index 0000000..39e6ec6
--- /dev/null
@@ -0,0 +1,26 @@
+set(SWIG_EXECUTABLE "swig3.0")
+find_package(SWIG 3.0 REQUIRED)
+include(${SWIG_USE_FILE})
+
+include_directories(${LIBDRM_INCLUDE_DIRS})
+link_directories(${LIBDRM_LIBRARY_DIRS})
+
+pkg_check_modules(PYTHON python-3.4 REQUIRED)
+include_directories(${PYTHON_INCLUDE_DIRS})
+
+#include_directories(libkms)
+#XXX
+include_directories(../libkms++)
+
+#set(CMAKE_SWIG_FLAGS "-I../../libkms")
+
+set_source_files_properties(pykms.i PROPERTIES CPLUSPLUS ON)
+swig_add_module(pykms python pykms.i)
+swig_link_libraries(pykms kms++ ${LIBDRM_LIBRARIES} ${PYTHON_LIBRARIES})
+
+add_custom_target(pyextras SOURCES test.py functest.py)
+
+add_test(NAME pytest COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/functest.py")
+set_property(TEST pytest PROPERTY
+        ENVIRONMENT "PYTHONPATH=." "LD_LIBRARY_PATH=."
+)
diff --git a/py/functest.py b/py/functest.py
new file mode 100755 (executable)
index 0000000..54c3363
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/python3.4
+
+import pykms
+
+card = pykms.Card()
+
+conn = card.get_first_connected_connector()
+
+mode = conn.get_default_mode()
+
+fb = pykms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+fb.draw_test_pattern(0);
+
+crtc = conn.get_current_crtc()
+
+crtc.set_mode(conn, fb, mode)
+
+print("OK")
+
diff --git a/py/pykms.i b/py/pykms.i
new file mode 100644 (file)
index 0000000..6a6ad11
--- /dev/null
@@ -0,0 +1,20 @@
+%module pykms
+%{
+#include "kms++.h"
+#include "utils/testpat.h"
+using namespace kms;
+%}
+
+%include "stdint.i"
+
+%include "decls.h"
+%include "drmobject.h"
+%include "atomicreq.h"
+%include "crtc.h"
+%include "card.h"
+%include "property.h"
+%include "framebuffer.h"
+%include "plane.h"
+%include "connector.h"
+%include "encoder.h"
+%include "utils/testpat.h"
diff --git a/py/test.py b/py/test.py
new file mode 100755 (executable)
index 0000000..a104ba5
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/python3.4
+
+import pykms
+
+card = pykms.Card()
+card.print_short()
+
+conn = card.get_first_connected_connector()
+
+mode = conn.get_default_mode()
+
+fb = pykms.Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+pykms.draw_test_pattern(fb);
+
+crtc = conn.get_current_crtc()
+
+crtc.set_mode(conn, fb, mode)
+
+input("press enter to exit\n")
diff --git a/test.h b/test.h
new file mode 100644 (file)
index 0000000..090ff38
--- /dev/null
+++ b/test.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+static void ASSERT_FAIL(const char *cond, const char *file,
+                          unsigned int line, const char *func) __attribute__ ((__noreturn__));
+
+static void ASSERT_FAIL(const char *cond, const char *file,
+                          unsigned int line, const char *func)
+{
+       fprintf(stderr, "%s:%d: %s: ASSERT(%s) failed\n", file, line, func, cond);
+       abort();
+}
+
+#define ASSERT(x) if (unlikely(!(x))) { ASSERT_FAIL( __STRING(x), __FILE__, __LINE__, __PRETTY_FUNCTION__); }
diff --git a/testpat/CMakeLists.txt b/testpat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..aed4371
--- /dev/null
@@ -0,0 +1,6 @@
+include_directories(${LIBDRM_INCLUDE_DIRS})
+link_directories(${LIBDRM_LIBRARY_DIRS})
+
+add_executable (testpat testpat.cpp)
+
+target_link_libraries(testpat kms++ ${LIBDRM_LIBRARIES})
diff --git a/testpat/testpat.cpp b/testpat/testpat.cpp
new file mode 100644 (file)
index 0000000..5632224
--- /dev/null
@@ -0,0 +1,88 @@
+#include <cstdio>
+#include <algorithm>
+
+#include "kms++.h"
+#include "utils/testpat.h"
+
+#include "../test.h"
+
+using namespace std;
+using namespace kms;
+
+int main()
+{
+       Card card;
+
+       if (card.master() == false)
+               printf("Not DRM master, modeset may fail\n");
+
+       //card.print_short();
+
+       auto connectors = card.get_connectors();
+
+       vector<Framebuffer*> fbs;
+       vector<Crtc*> used_crtcs;
+
+       for (auto conn : connectors)
+       {
+               if (conn->connected() == false)
+                       continue;
+
+               Crtc* crtc = conn->get_current_crtc();
+               if (!crtc) {
+                       vector<Crtc*> list = conn->get_possible_crtcs();
+                       for (auto c : list) {
+                               if (find(used_crtcs.begin(), used_crtcs.end(), c) == used_crtcs.end()) {
+                                       crtc = c;
+                                       break;
+                               }
+                       }
+               }
+               used_crtcs.push_back(crtc);
+
+               ASSERT(crtc);
+
+               int r;
+
+               // RG16 XR24 UYVY YUYV NV12
+
+               auto mode = conn->get_default_mode();
+
+               auto fb = new Framebuffer(card, mode.hdisplay, mode.vdisplay, "XR24");
+               draw_test_pattern(*fb);
+               fbs.push_back(fb);
+
+               r = crtc->set_mode(conn, *fb, mode);
+               ASSERT(r == 0);
+
+
+
+               Plane* plane = 0;
+
+               for (Plane* p : crtc->get_possible_planes()) {
+                       if (p->plane_type() == PlaneType::Overlay) {
+                               plane = p;
+                               break;
+                       }
+               }
+
+               if (plane) {
+                       auto planefb = new Framebuffer(card, 400, 400, "YUYV");
+                       draw_test_pattern(*planefb);
+                       fbs.push_back(planefb);
+
+                       r = crtc->set_plane(plane, *planefb,
+                                           0, 0, planefb->width(), planefb->height(),
+                                           0, 0, planefb->width(), planefb->height());
+
+                       ASSERT(r == 0);
+               }
+       }
+
+       printf("press enter to exit\n");
+
+       getchar();
+
+       for(auto fb : fbs)
+               delete fb;
+}