diff options
author | Alistair Strachan | 2018-09-27 14:31:56 -0500 |
---|---|---|
committer | Alistair Strachan | 2018-09-27 14:32:51 -0500 |
commit | e77f68c1d7a607019f9cf36d47b8f33fcb9b7146 (patch) | |
tree | 0ee57162c5dd375cf49b1699dac54289f520b012 | |
parent | c6e38de712579ec0145302f8f9e456ba1e7214f1 (diff) | |
parent | e0067bdc75566629c9143818c8f3970c16c8825e (diff) | |
download | external-libkmsxx-e77f68c1d7a607019f9cf36d47b8f33fcb9b7146.tar.gz external-libkmsxx-e77f68c1d7a607019f9cf36d47b8f33fcb9b7146.tar.xz external-libkmsxx-e77f68c1d7a607019f9cf36d47b8f33fcb9b7146.zip |
Merge commit 'e0067bdc75566629c9143818c8f3970c16c8825e'
Bug: 110964307
Bug: 116741782
Signed-off-by: Alistair Strachan <astrachan@google.com>
Change-Id: I2130490f8fa61743217cbf2081640773960ea843
128 files changed, 16604 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7efae50 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1,7 @@ | |||
1 | *.swp | ||
2 | *.a | ||
3 | *.so | ||
4 | build | ||
5 | *.txt.user | ||
6 | *.patch | ||
7 | py/__pycache__ | ||
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c6d1083 --- /dev/null +++ b/.gitmodules | |||
@@ -0,0 +1,3 @@ | |||
1 | [submodule "ext/pybind11"] | ||
2 | path = ext/pybind11 | ||
3 | url = https://github.com/pybind/pybind11.git | ||
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..353026a --- /dev/null +++ b/.travis.yml | |||
@@ -0,0 +1,81 @@ | |||
1 | # Ubuntu 14.04 Trusty support | ||
2 | sudo: required | ||
3 | dist: trusty | ||
4 | |||
5 | # Only build test master & travis | ||
6 | branches: | ||
7 | only: | ||
8 | - master | ||
9 | - travis | ||
10 | |||
11 | # Enable C++ support | ||
12 | language: cpp | ||
13 | |||
14 | addons: | ||
15 | apt: | ||
16 | sources: &sources | ||
17 | - ubuntu-toolchain-r-test | ||
18 | packages: &packages | ||
19 | - libegl1-mesa-dev | ||
20 | - libgles2-mesa-dev | ||
21 | - libwayland-dev | ||
22 | - libx11-xcb-dev | ||
23 | - libx11-dev | ||
24 | - libgbm-dev | ||
25 | |||
26 | # Need MYCC and MYCXX as travis overwrites CC and CXX | ||
27 | |||
28 | matrix: | ||
29 | include: | ||
30 | - compiler: gcc | ||
31 | addons: | ||
32 | apt: | ||
33 | sources: *sources | ||
34 | packages: [*packages, 'g++-4.8'] | ||
35 | env: MYCC=gcc-4.8 MYCXX=g++-4.8 | ||
36 | |||
37 | - compiler: gcc | ||
38 | addons: | ||
39 | apt: | ||
40 | sources: *sources | ||
41 | packages: [*packages, 'g++-4.9'] | ||
42 | # g++-4.9 gives a warning, disable it | ||
43 | env: MYCC=gcc-4.9 MYCXX=g++-4.9 CXXFLAGS=-Wno-maybe-uninitialized | ||
44 | |||
45 | - compiler: gcc | ||
46 | addons: | ||
47 | apt: | ||
48 | sources: *sources | ||
49 | packages: [*packages, 'g++-5'] | ||
50 | env: MYCC=gcc-5 MYCXX=g++-5 | ||
51 | |||
52 | - compiler: gcc | ||
53 | addons: | ||
54 | apt: | ||
55 | sources: *sources | ||
56 | packages: [*packages, 'g++-6'] | ||
57 | env: MYCC=gcc-6 MYCXX=g++-6 | ||
58 | |||
59 | - compiler: gcc | ||
60 | addons: | ||
61 | apt: | ||
62 | sources: *sources | ||
63 | packages: [*packages, 'g++-7'] | ||
64 | env: MYCC=gcc-7 MYCXX=g++-7 | ||
65 | |||
66 | - compiler: clang | ||
67 | addons: | ||
68 | apt: | ||
69 | sources: [*sources, 'llvm-toolchain-precise-3.8'] | ||
70 | packages: [*packages, 'clang-3.8'] | ||
71 | env: MYCC=clang-3.8 MYCXX=clang++-3.8 | ||
72 | |||
73 | # Build steps | ||
74 | script: | ||
75 | - mkdir build | ||
76 | - cd build | ||
77 | - CC=$MYCC CXX=$MYCXX cmake -DTREAT_WARNINGS_AS_ERRORS=1 -DKMSXX_ENABLE_KMSCUBE=1 -DKMSXX_ENABLE_PYTHON=1 .. && make VERBOSE=1 | ||
78 | |||
79 | notifications: | ||
80 | email: | ||
81 | - tomi.valkeinen@iki.fi | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..57088bc --- /dev/null +++ b/CMakeLists.txt | |||
@@ -0,0 +1,88 @@ | |||
1 | cmake_minimum_required(VERSION 2.8) | ||
2 | project(kms++) | ||
3 | |||
4 | include(LTO.cmake) | ||
5 | |||
6 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) | ||
7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) | ||
8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | ||
9 | |||
10 | include(CheckCXXCompilerFlag) | ||
11 | |||
12 | IF(NOT CMAKE_BUILD_TYPE) | ||
13 | message(STATUS "Setting build type to 'Release' as none was specified.") | ||
14 | SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) | ||
15 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") | ||
16 | ENDIF() | ||
17 | |||
18 | string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) | ||
19 | |||
20 | option(BUILD_SHARED_LIBS "Build shared libs" FALSE) | ||
21 | option(TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" FALSE) | ||
22 | |||
23 | set(KMSXX_ENABLE_PYTHON ON CACHE BOOL "Enable Python wrappers") | ||
24 | set(KMSXX_PYTHON_VERSION "python3;python2" CACHE STRING "Python pkgconfig package") | ||
25 | |||
26 | set(KMSXX_ENABLE_KMSCUBE OFF CACHE BOOL "Enable kmscube") | ||
27 | |||
28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall") | ||
29 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wno-unused-parameter") | ||
30 | |||
31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") | ||
32 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra -Wno-unused-parameter") | ||
33 | |||
34 | if (CMAKE_COMPILER_IS_GNUCC) | ||
35 | if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) | ||
36 | # GCC 4.x seems to warn too much | ||
37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-field-initializers") | ||
38 | endif() | ||
39 | endif() | ||
40 | |||
41 | # HACK: cmake always adds "-rdynamic", this removes it | ||
42 | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") | ||
43 | SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") | ||
44 | |||
45 | set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) | ||
46 | |||
47 | if (TREAT_WARNINGS_AS_ERRORS) | ||
48 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") | ||
49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") | ||
50 | endif() | ||
51 | |||
52 | # static link libc | ||
53 | # set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") | ||
54 | |||
55 | check_lto() | ||
56 | |||
57 | if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) | ||
58 | if (LTO_WORKS) | ||
59 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto") | ||
60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") | ||
61 | set(CMAKE_AR "${LTO_AR}") | ||
62 | set(CMAKE_RANLIB "${LTO_RANLIB}") | ||
63 | endif() | ||
64 | endif() | ||
65 | |||
66 | find_package(PkgConfig REQUIRED) | ||
67 | pkg_check_modules(LIBDRM libdrm>=2.4.64 REQUIRED) | ||
68 | |||
69 | pkg_check_modules(LIBDRM_OMAP libdrm_omap) | ||
70 | if(LIBDRM_OMAP_FOUND) | ||
71 | add_definitions(-DHAS_LIBDRM_OMAP) | ||
72 | endif() | ||
73 | |||
74 | enable_testing() | ||
75 | |||
76 | add_subdirectory(kms++) | ||
77 | add_subdirectory(kms++util) | ||
78 | add_subdirectory(utils) | ||
79 | |||
80 | if(KMSXX_ENABLE_KMSCUBE) | ||
81 | add_subdirectory(kmscube) | ||
82 | endif() | ||
83 | |||
84 | if(KMSXX_ENABLE_PYTHON) | ||
85 | add_subdirectory(py) | ||
86 | endif() | ||
87 | |||
88 | add_custom_target(docs SOURCES "README.md") | ||
@@ -0,0 +1,373 @@ | |||
1 | Mozilla Public License Version 2.0 | ||
2 | ================================== | ||
3 | |||
4 | 1. Definitions | ||
5 | -------------- | ||
6 | |||
7 | 1.1. "Contributor" | ||
8 | means each individual or legal entity that creates, contributes to | ||
9 | the creation of, or owns Covered Software. | ||
10 | |||
11 | 1.2. "Contributor Version" | ||
12 | means the combination of the Contributions of others (if any) used | ||
13 | by a Contributor and that particular Contributor's Contribution. | ||
14 | |||
15 | 1.3. "Contribution" | ||
16 | means Covered Software of a particular Contributor. | ||
17 | |||
18 | 1.4. "Covered Software" | ||
19 | means Source Code Form to which the initial Contributor has attached | ||
20 | the notice in Exhibit A, the Executable Form of such Source Code | ||
21 | Form, and Modifications of such Source Code Form, in each case | ||
22 | including portions thereof. | ||
23 | |||
24 | 1.5. "Incompatible With Secondary Licenses" | ||
25 | means | ||
26 | |||
27 | (a) that the initial Contributor has attached the notice described | ||
28 | in Exhibit B to the Covered Software; or | ||
29 | |||
30 | (b) that the Covered Software was made available under the terms of | ||
31 | version 1.1 or earlier of the License, but not also under the | ||
32 | terms of a Secondary License. | ||
33 | |||
34 | 1.6. "Executable Form" | ||
35 | means any form of the work other than Source Code Form. | ||
36 | |||
37 | 1.7. "Larger Work" | ||
38 | means a work that combines Covered Software with other material, in | ||
39 | a separate file or files, that is not Covered Software. | ||
40 | |||
41 | 1.8. "License" | ||
42 | means this document. | ||
43 | |||
44 | 1.9. "Licensable" | ||
45 | means having the right to grant, to the maximum extent possible, | ||
46 | whether at the time of the initial grant or subsequently, any and | ||
47 | all of the rights conveyed by this License. | ||
48 | |||
49 | 1.10. "Modifications" | ||
50 | means any of the following: | ||
51 | |||
52 | (a) any file in Source Code Form that results from an addition to, | ||
53 | deletion from, or modification of the contents of Covered | ||
54 | Software; or | ||
55 | |||
56 | (b) any new file in Source Code Form that contains any Covered | ||
57 | Software. | ||
58 | |||
59 | 1.11. "Patent Claims" of a Contributor | ||
60 | means any patent claim(s), including without limitation, method, | ||
61 | process, and apparatus claims, in any patent Licensable by such | ||
62 | Contributor that would be infringed, but for the grant of the | ||
63 | License, by the making, using, selling, offering for sale, having | ||
64 | made, import, or transfer of either its Contributions or its | ||
65 | Contributor Version. | ||
66 | |||
67 | 1.12. "Secondary License" | ||
68 | means either the GNU General Public License, Version 2.0, the GNU | ||
69 | Lesser General Public License, Version 2.1, the GNU Affero General | ||
70 | Public License, Version 3.0, or any later versions of those | ||
71 | licenses. | ||
72 | |||
73 | 1.13. "Source Code Form" | ||
74 | means the form of the work preferred for making modifications. | ||
75 | |||
76 | 1.14. "You" (or "Your") | ||
77 | means an individual or a legal entity exercising rights under this | ||
78 | License. For legal entities, "You" includes any entity that | ||
79 | controls, is controlled by, or is under common control with You. For | ||
80 | purposes of this definition, "control" means (a) the power, direct | ||
81 | or indirect, to cause the direction or management of such entity, | ||
82 | whether by contract or otherwise, or (b) ownership of more than | ||
83 | fifty percent (50%) of the outstanding shares or beneficial | ||
84 | ownership of such entity. | ||
85 | |||
86 | 2. License Grants and Conditions | ||
87 | -------------------------------- | ||
88 | |||
89 | 2.1. Grants | ||
90 | |||
91 | Each Contributor hereby grants You a world-wide, royalty-free, | ||
92 | non-exclusive license: | ||
93 | |||
94 | (a) under intellectual property rights (other than patent or trademark) | ||
95 | Licensable by such Contributor to use, reproduce, make available, | ||
96 | modify, display, perform, distribute, and otherwise exploit its | ||
97 | Contributions, either on an unmodified basis, with Modifications, or | ||
98 | as part of a Larger Work; and | ||
99 | |||
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer | ||
101 | for sale, have made, import, and otherwise transfer either its | ||
102 | Contributions or its Contributor Version. | ||
103 | |||
104 | 2.2. Effective Date | ||
105 | |||
106 | The licenses granted in Section 2.1 with respect to any Contribution | ||
107 | become effective for each Contribution on the date the Contributor first | ||
108 | distributes such Contribution. | ||
109 | |||
110 | 2.3. Limitations on Grant Scope | ||
111 | |||
112 | The licenses granted in this Section 2 are the only rights granted under | ||
113 | this License. No additional rights or licenses will be implied from the | ||
114 | distribution or licensing of Covered Software under this License. | ||
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||
116 | Contributor: | ||
117 | |||
118 | (a) for any code that a Contributor has removed from Covered Software; | ||
119 | or | ||
120 | |||
121 | (b) for infringements caused by: (i) Your and any other third party's | ||
122 | modifications of Covered Software, or (ii) the combination of its | ||
123 | Contributions with other software (except as part of its Contributor | ||
124 | Version); or | ||
125 | |||
126 | (c) under Patent Claims infringed by Covered Software in the absence of | ||
127 | its Contributions. | ||
128 | |||
129 | This License does not grant any rights in the trademarks, service marks, | ||
130 | or logos of any Contributor (except as may be necessary to comply with | ||
131 | the notice requirements in Section 3.4). | ||
132 | |||
133 | 2.4. Subsequent Licenses | ||
134 | |||
135 | No Contributor makes additional grants as a result of Your choice to | ||
136 | distribute the Covered Software under a subsequent version of this | ||
137 | License (see Section 10.2) or under the terms of a Secondary License (if | ||
138 | permitted under the terms of Section 3.3). | ||
139 | |||
140 | 2.5. Representation | ||
141 | |||
142 | Each Contributor represents that the Contributor believes its | ||
143 | Contributions are its original creation(s) or it has sufficient rights | ||
144 | to grant the rights to its Contributions conveyed by this License. | ||
145 | |||
146 | 2.6. Fair Use | ||
147 | |||
148 | This License is not intended to limit any rights You have under | ||
149 | applicable copyright doctrines of fair use, fair dealing, or other | ||
150 | equivalents. | ||
151 | |||
152 | 2.7. Conditions | ||
153 | |||
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||
155 | in Section 2.1. | ||
156 | |||
157 | 3. Responsibilities | ||
158 | ------------------- | ||
159 | |||
160 | 3.1. Distribution of Source Form | ||
161 | |||
162 | All distribution of Covered Software in Source Code Form, including any | ||
163 | Modifications that You create or to which You contribute, must be under | ||
164 | the terms of this License. You must inform recipients that the Source | ||
165 | Code Form of the Covered Software is governed by the terms of this | ||
166 | License, and how they can obtain a copy of this License. You may not | ||
167 | attempt to alter or restrict the recipients' rights in the Source Code | ||
168 | Form. | ||
169 | |||
170 | 3.2. Distribution of Executable Form | ||
171 | |||
172 | If You distribute Covered Software in Executable Form then: | ||
173 | |||
174 | (a) such Covered Software must also be made available in Source Code | ||
175 | Form, as described in Section 3.1, and You must inform recipients of | ||
176 | the Executable Form how they can obtain a copy of such Source Code | ||
177 | Form by reasonable means in a timely manner, at a charge no more | ||
178 | than the cost of distribution to the recipient; and | ||
179 | |||
180 | (b) You may distribute such Executable Form under the terms of this | ||
181 | License, or sublicense it under different terms, provided that the | ||
182 | license for the Executable Form does not attempt to limit or alter | ||
183 | the recipients' rights in the Source Code Form under this License. | ||
184 | |||
185 | 3.3. Distribution of a Larger Work | ||
186 | |||
187 | You may create and distribute a Larger Work under terms of Your choice, | ||
188 | provided that You also comply with the requirements of this License for | ||
189 | the Covered Software. If the Larger Work is a combination of Covered | ||
190 | Software with a work governed by one or more Secondary Licenses, and the | ||
191 | Covered Software is not Incompatible With Secondary Licenses, this | ||
192 | License permits You to additionally distribute such Covered Software | ||
193 | under the terms of such Secondary License(s), so that the recipient of | ||
194 | the Larger Work may, at their option, further distribute the Covered | ||
195 | Software under the terms of either this License or such Secondary | ||
196 | License(s). | ||
197 | |||
198 | 3.4. Notices | ||
199 | |||
200 | You may not remove or alter the substance of any license notices | ||
201 | (including copyright notices, patent notices, disclaimers of warranty, | ||
202 | or limitations of liability) contained within the Source Code Form of | ||
203 | the Covered Software, except that You may alter any license notices to | ||
204 | the extent required to remedy known factual inaccuracies. | ||
205 | |||
206 | 3.5. Application of Additional Terms | ||
207 | |||
208 | You may choose to offer, and to charge a fee for, warranty, support, | ||
209 | indemnity or liability obligations to one or more recipients of Covered | ||
210 | Software. However, You may do so only on Your own behalf, and not on | ||
211 | behalf of any Contributor. You must make it absolutely clear that any | ||
212 | such warranty, support, indemnity, or liability obligation is offered by | ||
213 | You alone, and You hereby agree to indemnify every Contributor for any | ||
214 | liability incurred by such Contributor as a result of warranty, support, | ||
215 | indemnity or liability terms You offer. You may include additional | ||
216 | disclaimers of warranty and limitations of liability specific to any | ||
217 | jurisdiction. | ||
218 | |||
219 | 4. Inability to Comply Due to Statute or Regulation | ||
220 | --------------------------------------------------- | ||
221 | |||
222 | If it is impossible for You to comply with any of the terms of this | ||
223 | License with respect to some or all of the Covered Software due to | ||
224 | statute, judicial order, or regulation then You must: (a) comply with | ||
225 | the terms of this License to the maximum extent possible; and (b) | ||
226 | describe the limitations and the code they affect. Such description must | ||
227 | be placed in a text file included with all distributions of the Covered | ||
228 | Software under this License. Except to the extent prohibited by statute | ||
229 | or regulation, such description must be sufficiently detailed for a | ||
230 | recipient of ordinary skill to be able to understand it. | ||
231 | |||
232 | 5. Termination | ||
233 | -------------- | ||
234 | |||
235 | 5.1. The rights granted under this License will terminate automatically | ||
236 | if You fail to comply with any of its terms. However, if You become | ||
237 | compliant, then the rights granted under this License from a particular | ||
238 | Contributor are reinstated (a) provisionally, unless and until such | ||
239 | Contributor explicitly and finally terminates Your grants, and (b) on an | ||
240 | ongoing basis, if such Contributor fails to notify You of the | ||
241 | non-compliance by some reasonable means prior to 60 days after You have | ||
242 | come back into compliance. Moreover, Your grants from a particular | ||
243 | Contributor are reinstated on an ongoing basis if such Contributor | ||
244 | notifies You of the non-compliance by some reasonable means, this is the | ||
245 | first time You have received notice of non-compliance with this License | ||
246 | from such Contributor, and You become compliant prior to 30 days after | ||
247 | Your receipt of the notice. | ||
248 | |||
249 | 5.2. If You initiate litigation against any entity by asserting a patent | ||
250 | infringement claim (excluding declaratory judgment actions, | ||
251 | counter-claims, and cross-claims) alleging that a Contributor Version | ||
252 | directly or indirectly infringes any patent, then the rights granted to | ||
253 | You by any and all Contributors for the Covered Software under Section | ||
254 | 2.1 of this License shall terminate. | ||
255 | |||
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | ||
257 | end user license agreements (excluding distributors and resellers) which | ||
258 | have been validly granted by You or Your distributors under this License | ||
259 | prior to termination shall survive termination. | ||
260 | |||
261 | ************************************************************************ | ||
262 | * * | ||
263 | * 6. Disclaimer of Warranty * | ||
264 | * ------------------------- * | ||
265 | * * | ||
266 | * Covered Software is provided under this License on an "as is" * | ||
267 | * basis, without warranty of any kind, either expressed, implied, or * | ||
268 | * statutory, including, without limitation, warranties that the * | ||
269 | * Covered Software is free of defects, merchantable, fit for a * | ||
270 | * particular purpose or non-infringing. The entire risk as to the * | ||
271 | * quality and performance of the Covered Software is with You. * | ||
272 | * Should any Covered Software prove defective in any respect, You * | ||
273 | * (not any Contributor) assume the cost of any necessary servicing, * | ||
274 | * repair, or correction. This disclaimer of warranty constitutes an * | ||
275 | * essential part of this License. No use of any Covered Software is * | ||
276 | * authorized under this License except under this disclaimer. * | ||
277 | * * | ||
278 | ************************************************************************ | ||
279 | |||
280 | ************************************************************************ | ||
281 | * * | ||
282 | * 7. Limitation of Liability * | ||
283 | * -------------------------- * | ||
284 | * * | ||
285 | * Under no circumstances and under no legal theory, whether tort * | ||
286 | * (including negligence), contract, or otherwise, shall any * | ||
287 | * Contributor, or anyone who distributes Covered Software as * | ||
288 | * permitted above, be liable to You for any direct, indirect, * | ||
289 | * special, incidental, or consequential damages of any character * | ||
290 | * including, without limitation, damages for lost profits, loss of * | ||
291 | * goodwill, work stoppage, computer failure or malfunction, or any * | ||
292 | * and all other commercial damages or losses, even if such party * | ||
293 | * shall have been informed of the possibility of such damages. This * | ||
294 | * limitation of liability shall not apply to liability for death or * | ||
295 | * personal injury resulting from such party's negligence to the * | ||
296 | * extent applicable law prohibits such limitation. Some * | ||
297 | * jurisdictions do not allow the exclusion or limitation of * | ||
298 | * incidental or consequential damages, so this exclusion and * | ||
299 | * limitation may not apply to You. * | ||
300 | * * | ||
301 | ************************************************************************ | ||
302 | |||
303 | 8. Litigation | ||
304 | ------------- | ||
305 | |||
306 | Any litigation relating to this License may be brought only in the | ||
307 | courts of a jurisdiction where the defendant maintains its principal | ||
308 | place of business and such litigation shall be governed by laws of that | ||
309 | jurisdiction, without reference to its conflict-of-law provisions. | ||
310 | Nothing in this Section shall prevent a party's ability to bring | ||
311 | cross-claims or counter-claims. | ||
312 | |||
313 | 9. Miscellaneous | ||
314 | ---------------- | ||
315 | |||
316 | This License represents the complete agreement concerning the subject | ||
317 | matter hereof. If any provision of this License is held to be | ||
318 | unenforceable, such provision shall be reformed only to the extent | ||
319 | necessary to make it enforceable. Any law or regulation which provides | ||
320 | that the language of a contract shall be construed against the drafter | ||
321 | shall not be used to construe this License against a Contributor. | ||
322 | |||
323 | 10. Versions of the License | ||
324 | --------------------------- | ||
325 | |||
326 | 10.1. New Versions | ||
327 | |||
328 | Mozilla Foundation is the license steward. Except as provided in Section | ||
329 | 10.3, no one other than the license steward has the right to modify or | ||
330 | publish new versions of this License. Each version will be given a | ||
331 | distinguishing version number. | ||
332 | |||
333 | 10.2. Effect of New Versions | ||
334 | |||
335 | You may distribute the Covered Software under the terms of the version | ||
336 | of the License under which You originally received the Covered Software, | ||
337 | or under the terms of any subsequent version published by the license | ||
338 | steward. | ||
339 | |||
340 | 10.3. Modified Versions | ||
341 | |||
342 | If you create software not governed by this License, and you want to | ||
343 | create a new license for such software, you may create and use a | ||
344 | modified version of this License if you rename the license and remove | ||
345 | any references to the name of the license steward (except to note that | ||
346 | such modified license differs from this License). | ||
347 | |||
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||
349 | Licenses | ||
350 | |||
351 | If You choose to distribute Source Code Form that is Incompatible With | ||
352 | Secondary Licenses under the terms of this version of the License, the | ||
353 | notice described in Exhibit B of this License must be attached. | ||
354 | |||
355 | Exhibit A - Source Code Form License Notice | ||
356 | ------------------------------------------- | ||
357 | |||
358 | This Source Code Form is subject to the terms of the Mozilla Public | ||
359 | License, v. 2.0. If a copy of the MPL was not distributed with this | ||
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
361 | |||
362 | If it is not possible or desirable to put the notice in a particular | ||
363 | file, then You may include the notice in a location (such as a LICENSE | ||
364 | file in a relevant directory) where a recipient would be likely to look | ||
365 | for such a notice. | ||
366 | |||
367 | You may add additional accurate notices of copyright ownership. | ||
368 | |||
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||
370 | --------------------------------------------------------- | ||
371 | |||
372 | This Source Code Form is "Incompatible With Secondary Licenses", as | ||
373 | defined by the Mozilla Public License, v. 2.0. | ||
diff --git a/LTO.cmake b/LTO.cmake new file mode 100644 index 0000000..9a18d77 --- /dev/null +++ b/LTO.cmake | |||
@@ -0,0 +1,32 @@ | |||
1 | function(check_lto) | ||
2 | if (DEFINED LTO_WORKS) | ||
3 | return() | ||
4 | endif() | ||
5 | |||
6 | set(LTO_WORKS FALSE CACHE INTERNAL "LTO works") | ||
7 | |||
8 | CHECK_CXX_COMPILER_FLAG("-flto" HAS_LTO_FLAG) | ||
9 | |||
10 | if (NOT HAS_LTO_FLAG) | ||
11 | return() | ||
12 | endif() | ||
13 | |||
14 | find_program(LTO_AR NAMES "${CMAKE_C_COMPILER}-ar" gcc-ar) | ||
15 | find_program(LTO_RANLIB NAMES "${CMAKE_C_COMPILER}-ranlib" gcc-ranlib) | ||
16 | |||
17 | if (NOT LTO_AR OR NOT LTO_RANLIB) | ||
18 | return() | ||
19 | endif() | ||
20 | |||
21 | EXECUTE_PROCESS(COMMAND "${LTO_AR}" --version RESULT_VARIABLE ret OUTPUT_QUIET ERROR_QUIET) | ||
22 | if (ret) | ||
23 | return() | ||
24 | endif() | ||
25 | |||
26 | EXECUTE_PROCESS(COMMAND "${LTO_RANLIB}" --version RESULT_VARIABLE ret OUTPUT_QUIET ERROR_QUIET) | ||
27 | if (ret) | ||
28 | return() | ||
29 | endif() | ||
30 | |||
31 | set(LTO_WORKS TRUE CACHE INTERNAL "LTO works") | ||
32 | endfunction() | ||
diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0f3b97 --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,99 @@ | |||
1 | [![Build Status](https://travis-ci.org/tomba/kmsxx.svg?branch=master)](https://travis-ci.org/tomba/kmsxx) | ||
2 | |||
3 | # kms++ - C++ library for kernel mode setting | ||
4 | |||
5 | kms++ is a C++11 library for kernel mode setting. | ||
6 | |||
7 | Also included are some simple utilities for KMS and python bindings for kms++. | ||
8 | |||
9 | ## Utilities | ||
10 | |||
11 | - kmstest - set modes and planes and show test pattern on crtcs/planes, and test page flips | ||
12 | - kmsprint - print information about DRM objects | ||
13 | - kmsview - view raw images | ||
14 | - kmscube - rotating 3D cube on crtcs/planes | ||
15 | - kmscapture - show captured frames from a camera on screen | ||
16 | |||
17 | ## Dependencies: | ||
18 | |||
19 | - libdrm | ||
20 | - Python 3.x (for python bindings) | ||
21 | |||
22 | ## Build instructions: | ||
23 | |||
24 | To build the Python bindings you need to set up the git-submodule for pybind11: | ||
25 | |||
26 | ``` | ||
27 | git submodule update --init | ||
28 | ``` | ||
29 | |||
30 | And to compile: | ||
31 | |||
32 | ``` | ||
33 | $ mkdir build | ||
34 | $ cd build | ||
35 | $ cmake .. | ||
36 | $ make -j4 | ||
37 | ``` | ||
38 | |||
39 | ## Cross compiling instructions: | ||
40 | |||
41 | Directions for cross compiling depend on your environment. | ||
42 | |||
43 | These are for mine with buildroot: | ||
44 | |||
45 | ``` | ||
46 | $ mkdir build | ||
47 | $ cd build | ||
48 | $ cmake -DCMAKE_TOOLCHAIN_FILE=<buildrootpath>/output/host/usr/share/buildroot/toolchainfile.cmake .. | ||
49 | $ make -j4 | ||
50 | ``` | ||
51 | |||
52 | Your environment may provide similar toolchainfile. If not, you can create a toolchainfile of your own, something along these lines: | ||
53 | |||
54 | ``` | ||
55 | SET(CMAKE_SYSTEM_NAME Linux) | ||
56 | |||
57 | SET(BROOT "<buildroot>/output/") | ||
58 | |||
59 | # specify the cross compiler | ||
60 | SET(CMAKE_C_COMPILER ${BROOT}/host/usr/bin/arm-buildroot-linux-gnueabihf-gcc) | ||
61 | SET(CMAKE_CXX_COMPILER ${BROOT}/host/usr/bin/arm-buildroot-linux-gnueabihf-g++) | ||
62 | |||
63 | # where is the target environment | ||
64 | SET(CMAKE_FIND_ROOT_PATH ${BROOT}/target ${BROOT}/host) | ||
65 | |||
66 | SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) | ||
67 | SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | ||
68 | SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) | ||
69 | ``` | ||
70 | |||
71 | ## Build options | ||
72 | |||
73 | You can use the following cmake flags to control the build. Use `-DFLAG=VALUE` to set them. | ||
74 | |||
75 | Option name | Values | Default | Notes | ||
76 | --------------------- | ------------- | --------------- | -------- | ||
77 | CMAKE_BUILD_TYPE | Release/Debug | Release | | ||
78 | BUILD_SHARED_LIBS | ON/OFF | OFF | | ||
79 | KMSXX_ENABLE_PYTHON | ON/OFF | ON | | ||
80 | KMSXX_ENABLE_KMSCUBE | ON/OFF | OFF | | ||
81 | KMSXX_PYTHON_VERSION | python3/python2 | python3;python2 | Name of the python pkgconfig file | ||
82 | |||
83 | ## Env variables | ||
84 | |||
85 | You can use the following runtime environmental variables to control the behavior of kms++. | ||
86 | |||
87 | Variable | Description | ||
88 | --------------------------------- | ------------- | ||
89 | KMSXX_DISABLE_UNIVERSAL_PLANES | Set to disable the use of universal planes | ||
90 | KMSXX_DISABLE_ATOMIC | Set to disable the use of atomic modesetting | ||
91 | |||
92 | ## Python notes | ||
93 | |||
94 | You can run the python code directly from the build dir by defining PYTHONPATH env variable. For example: | ||
95 | |||
96 | ``` | ||
97 | PYTHONPATH=build/py py/tests/hpd.py | ||
98 | |||
99 | ``` | ||
@@ -0,0 +1,8 @@ | |||
1 | |||
2 | C++ move semantics for classes (especially for Framebuffers)? | ||
3 | |||
4 | Proper error checks and use of exceptions | ||
5 | |||
6 | Free drmModeConnectorPtr (and similar for other drm objects) after use, instead | ||
7 | of storing it. | ||
8 | |||
diff --git a/ext/pybind11 b/ext/pybind11 new file mode 160000 | |||
Subproject 086d53e8c66a84d0ec723d5435918c76edd878e | |||
diff --git a/kms++/CMakeLists.txt b/kms++/CMakeLists.txt new file mode 100644 index 0000000..10352a2 --- /dev/null +++ b/kms++/CMakeLists.txt | |||
@@ -0,0 +1,33 @@ | |||
1 | include_directories(${LIBDRM_INCLUDE_DIRS}) | ||
2 | link_directories(${LIBDRM_LIBRARY_DIRS}) | ||
3 | |||
4 | include_directories(${LIBDRM_OMAP_INCLUDE_DIRS}) | ||
5 | link_directories(${LIBDRM_OMAP_LIBRARY_DIRS}) | ||
6 | |||
7 | file(GLOB SRCS "src/*.cpp" "src/*.h") | ||
8 | file(GLOB PUB_HDRS "inc/kms++/*.h") | ||
9 | |||
10 | if(LIBDRM_OMAP_FOUND) | ||
11 | file(GLOB OMAP_SRCS "src/omap/*.cpp" "src/omap/*.h") | ||
12 | file(GLOB OMAP_PUB_HDRS "inc/kms++/omap/*.h") | ||
13 | |||
14 | set(SRCS ${SRCS} ${OMAP_SRCS}) | ||
15 | set(PUB_HDRS ${PUB_HDRS} ${OMAP_PUB_HDRS}) | ||
16 | endif() | ||
17 | |||
18 | add_library(kms++ ${SRCS} ${PUB_HDRS}) | ||
19 | |||
20 | target_include_directories(kms++ PUBLIC | ||
21 | $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> | ||
22 | $<INSTALL_INTERFACE:include> | ||
23 | PRIVATE src) | ||
24 | |||
25 | target_link_libraries(kms++ ${LIBDRM_LIBRARIES} ${LIBDRM_OMAP_LIBRARIES}) | ||
26 | |||
27 | set_target_properties(kms++ PROPERTIES | ||
28 | PUBLIC_HEADER "${PUB_HDRS}") | ||
29 | |||
30 | install(TARGETS kms++ | ||
31 | LIBRARY DESTINATION lib | ||
32 | ARCHIVE DESTINATION lib | ||
33 | PUBLIC_HEADER DESTINATION include/kms++) | ||
diff --git a/kms++/inc/kms++/atomicreq.h b/kms++/inc/kms++/atomicreq.h new file mode 100644 index 0000000..a678b54 --- /dev/null +++ b/kms++/inc/kms++/atomicreq.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <cstdint> | ||
4 | #include <string> | ||
5 | #include <map> | ||
6 | |||
7 | struct _drmModeAtomicReq; | ||
8 | |||
9 | #include "decls.h" | ||
10 | |||
11 | namespace kms | ||
12 | { | ||
13 | class AtomicReq | ||
14 | { | ||
15 | public: | ||
16 | AtomicReq(Card& card); | ||
17 | ~AtomicReq(); | ||
18 | |||
19 | AtomicReq(const AtomicReq& other) = delete; | ||
20 | AtomicReq& operator=(const AtomicReq& other) = delete; | ||
21 | |||
22 | void add(uint32_t ob_id, uint32_t prop_id, uint64_t value); | ||
23 | void add(DrmPropObject *ob, Property *prop, uint64_t value); | ||
24 | void add(DrmPropObject *ob, const std::string& prop, uint64_t value); | ||
25 | void add(DrmPropObject *ob, const std::map<std::string, uint64_t>& values); | ||
26 | |||
27 | void add_display(Connector* conn, Crtc* crtc, Blob* videomode, | ||
28 | Plane* primary, Framebuffer* fb); | ||
29 | |||
30 | int test(bool allow_modeset = false); | ||
31 | int commit(void* data, bool allow_modeset = false); | ||
32 | int commit_sync(bool allow_modeset = false); | ||
33 | |||
34 | private: | ||
35 | Card& m_card; | ||
36 | _drmModeAtomicReq* m_req; | ||
37 | }; | ||
38 | |||
39 | } | ||
diff --git a/kms++/inc/kms++/blob.h b/kms++/inc/kms++/blob.h new file mode 100644 index 0000000..fd872f1 --- /dev/null +++ b/kms++/inc/kms++/blob.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "drmobject.h" | ||
4 | #include <vector> | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | |||
9 | class Blob : public DrmObject | ||
10 | { | ||
11 | public: | ||
12 | Blob(Card& card, uint32_t blob_id); | ||
13 | Blob(Card& card, void* data, size_t len); | ||
14 | virtual ~Blob(); | ||
15 | |||
16 | std::vector<uint8_t> data(); | ||
17 | |||
18 | private: | ||
19 | bool m_created; | ||
20 | }; | ||
21 | |||
22 | } | ||
diff --git a/kms++/inc/kms++/card.h b/kms++/inc/kms++/card.h new file mode 100644 index 0000000..c86278d --- /dev/null +++ b/kms++/inc/kms++/card.h | |||
@@ -0,0 +1,72 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <cstdint> | ||
4 | #include <vector> | ||
5 | #include <map> | ||
6 | |||
7 | #include "decls.h" | ||
8 | #include "pipeline.h" | ||
9 | |||
10 | namespace kms | ||
11 | { | ||
12 | class Card | ||
13 | { | ||
14 | friend class Framebuffer; | ||
15 | public: | ||
16 | Card(); | ||
17 | Card(const std::string& device); | ||
18 | virtual ~Card(); | ||
19 | |||
20 | Card(const Card& other) = delete; | ||
21 | Card& operator=(const Card& other) = delete; | ||
22 | |||
23 | int fd() const { return m_fd; } | ||
24 | |||
25 | void drop_master(); | ||
26 | |||
27 | Connector* get_first_connected_connector() const; | ||
28 | |||
29 | DrmObject* get_object(uint32_t id) const; | ||
30 | Connector* get_connector(uint32_t id) const; | ||
31 | Crtc* get_crtc(uint32_t id) const; | ||
32 | Encoder* get_encoder(uint32_t id) const; | ||
33 | Plane* get_plane(uint32_t id) const; | ||
34 | Property* get_prop(uint32_t id) const; | ||
35 | |||
36 | bool master() const { return m_master; } | ||
37 | bool has_atomic() const { return m_has_atomic; } | ||
38 | bool has_has_universal_planes() const { return m_has_universal_planes; } | ||
39 | |||
40 | const std::vector<Connector*> get_connectors() const { return m_connectors; } | ||
41 | const std::vector<Encoder*> get_encoders() const { return m_encoders; } | ||
42 | const std::vector<Crtc*> get_crtcs() const { return m_crtcs; } | ||
43 | const std::vector<Plane*> get_planes() const { return m_planes; } | ||
44 | const std::vector<Property*> get_properties() const { return m_properties; } | ||
45 | |||
46 | const std::vector<DrmObject*> get_objects() const; | ||
47 | |||
48 | std::vector<Pipeline> get_connected_pipelines(); | ||
49 | |||
50 | void call_page_flip_handlers(); | ||
51 | |||
52 | int disable_all(); | ||
53 | |||
54 | private: | ||
55 | void restore_modes(); | ||
56 | |||
57 | std::map<uint32_t, DrmObject*> m_obmap; | ||
58 | |||
59 | std::vector<Connector*> m_connectors; | ||
60 | std::vector<Encoder*> m_encoders; | ||
61 | std::vector<Crtc*> m_crtcs; | ||
62 | std::vector<Plane*> m_planes; | ||
63 | std::vector<Property*> m_properties; | ||
64 | std::vector<Framebuffer*> m_framebuffers; | ||
65 | |||
66 | int m_fd; | ||
67 | bool m_master; | ||
68 | |||
69 | bool m_has_atomic; | ||
70 | bool m_has_universal_planes; | ||
71 | }; | ||
72 | } | ||
diff --git a/kms++/inc/kms++/connector.h b/kms++/inc/kms++/connector.h new file mode 100644 index 0000000..3407730 --- /dev/null +++ b/kms++/inc/kms++/connector.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <vector> | ||
4 | |||
5 | #include "drmpropobject.h" | ||
6 | #include "videomode.h" | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | |||
11 | struct ConnectorPriv; | ||
12 | |||
13 | class Connector : public DrmPropObject | ||
14 | { | ||
15 | friend class Card; | ||
16 | public: | ||
17 | void refresh(); | ||
18 | |||
19 | Videomode get_default_mode() const; | ||
20 | |||
21 | Videomode get_mode(const std::string& mode) const; | ||
22 | Videomode get_mode(unsigned xres, unsigned yres, float vrefresh, bool ilace) const; | ||
23 | |||
24 | Crtc* get_current_crtc() const; | ||
25 | std::vector<Crtc*> get_possible_crtcs() const; | ||
26 | |||
27 | bool connected() const; | ||
28 | |||
29 | const std::string& fullname() const { return m_fullname; } | ||
30 | uint32_t connector_type() const; | ||
31 | uint32_t connector_type_id() const; | ||
32 | uint32_t mmWidth() const; | ||
33 | uint32_t mmHeight() const; | ||
34 | uint32_t subpixel() const; | ||
35 | const std::string& subpixel_str() const; | ||
36 | std::vector<Videomode> get_modes() const; | ||
37 | std::vector<Encoder*> get_encoders() const; | ||
38 | private: | ||
39 | Connector(Card& card, uint32_t id, uint32_t idx); | ||
40 | ~Connector(); | ||
41 | |||
42 | void setup(); | ||
43 | void restore_mode(); | ||
44 | |||
45 | ConnectorPriv* m_priv; | ||
46 | |||
47 | std::string m_fullname; | ||
48 | |||
49 | Encoder* m_current_encoder; | ||
50 | |||
51 | Crtc* m_saved_crtc; | ||
52 | }; | ||
53 | } | ||
diff --git a/kms++/inc/kms++/crtc.h b/kms++/inc/kms++/crtc.h new file mode 100644 index 0000000..ea20ef8 --- /dev/null +++ b/kms++/inc/kms++/crtc.h | |||
@@ -0,0 +1,54 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <vector> | ||
4 | |||
5 | #include "drmpropobject.h" | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | |||
10 | struct CrtcPriv; | ||
11 | |||
12 | class Crtc : public DrmPropObject | ||
13 | { | ||
14 | friend class Card; | ||
15 | friend class Connector; | ||
16 | public: | ||
17 | void refresh(); | ||
18 | |||
19 | const std::vector<Plane*>& get_possible_planes() const { return m_possible_planes; } | ||
20 | |||
21 | int set_mode(Connector* conn, const Videomode& mode); | ||
22 | int set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode); | ||
23 | |||
24 | int set_plane(Plane *plane, Framebuffer &fb, | ||
25 | int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, | ||
26 | float src_x, float src_y, float src_w, float src_h); | ||
27 | int disable_mode(); | ||
28 | |||
29 | int disable_plane(Plane* plane); | ||
30 | |||
31 | Plane* get_primary_plane(); | ||
32 | |||
33 | int page_flip(Framebuffer& fb, void *data); | ||
34 | |||
35 | uint32_t buffer_id() const; | ||
36 | uint32_t x() const; | ||
37 | uint32_t y() const; | ||
38 | uint32_t width() const; | ||
39 | uint32_t height() const; | ||
40 | int mode_valid() const; | ||
41 | Videomode mode() const; | ||
42 | int gamma_size() const; | ||
43 | private: | ||
44 | Crtc(Card& card, uint32_t id, uint32_t idx); | ||
45 | ~Crtc(); | ||
46 | |||
47 | void setup(); | ||
48 | void restore_mode(Connector *conn); | ||
49 | |||
50 | CrtcPriv* m_priv; | ||
51 | |||
52 | std::vector<Plane*> m_possible_planes; | ||
53 | }; | ||
54 | } | ||
diff --git a/kms++/inc/kms++/decls.h b/kms++/inc/kms++/decls.h new file mode 100644 index 0000000..91bce13 --- /dev/null +++ b/kms++/inc/kms++/decls.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #pragma once | ||
2 | |||
3 | namespace kms | ||
4 | { | ||
5 | class AtomicReq; | ||
6 | class Blob; | ||
7 | class Card; | ||
8 | class Connector; | ||
9 | class Crtc; | ||
10 | class DrmObject; | ||
11 | class DrmPropObject; | ||
12 | class DumbFramebuffer; | ||
13 | class Encoder; | ||
14 | class ExtFramebuffer; | ||
15 | class Framebuffer; | ||
16 | class PageFlipHandlerBase; | ||
17 | class Plane; | ||
18 | class Property; | ||
19 | struct Videomode; | ||
20 | } | ||
diff --git a/kms++/inc/kms++/drmobject.h b/kms++/inc/kms++/drmobject.h new file mode 100644 index 0000000..a939aa7 --- /dev/null +++ b/kms++/inc/kms++/drmobject.h | |||
@@ -0,0 +1,40 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <map> | ||
4 | |||
5 | #include "decls.h" | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | |||
10 | class DrmObject | ||
11 | { | ||
12 | friend class Card; | ||
13 | public: | ||
14 | DrmObject(const DrmObject& other) = delete; | ||
15 | DrmObject& operator=(const DrmObject& other) = delete; | ||
16 | |||
17 | uint32_t id() const { return m_id; } | ||
18 | Card& card() const { return m_card; } | ||
19 | |||
20 | uint32_t object_type() const { return m_object_type; } | ||
21 | uint32_t idx() const { return m_idx; } | ||
22 | |||
23 | protected: | ||
24 | DrmObject(Card& card, uint32_t object_type); | ||
25 | DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx = 0); | ||
26 | |||
27 | virtual ~DrmObject(); | ||
28 | |||
29 | virtual void setup() { } | ||
30 | |||
31 | virtual void set_id(uint32_t id); | ||
32 | |||
33 | private: | ||
34 | Card& m_card; | ||
35 | |||
36 | uint32_t m_id; | ||
37 | uint32_t m_object_type; | ||
38 | uint32_t m_idx; | ||
39 | }; | ||
40 | } | ||
diff --git a/kms++/inc/kms++/drmpropobject.h b/kms++/inc/kms++/drmpropobject.h new file mode 100644 index 0000000..38de584 --- /dev/null +++ b/kms++/inc/kms++/drmpropobject.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <map> | ||
4 | #include <memory> | ||
5 | |||
6 | #include "drmobject.h" | ||
7 | #include "decls.h" | ||
8 | |||
9 | namespace kms | ||
10 | { | ||
11 | |||
12 | class DrmPropObject : public DrmObject | ||
13 | { | ||
14 | friend class Card; | ||
15 | public: | ||
16 | void refresh_props(); | ||
17 | |||
18 | Property* get_prop(const std::string& name) const; | ||
19 | |||
20 | uint64_t get_prop_value(uint32_t id) const; | ||
21 | uint64_t get_prop_value(const std::string& name) const; | ||
22 | std::unique_ptr<Blob> get_prop_value_as_blob(const std::string& name) const; | ||
23 | |||
24 | const std::map<uint32_t, uint64_t>& get_prop_map() const { return m_prop_values; } | ||
25 | |||
26 | int set_prop_value(uint32_t id, uint64_t value); | ||
27 | int set_prop_value(const std::string& name, uint64_t value); | ||
28 | |||
29 | protected: | ||
30 | DrmPropObject(Card& card, uint32_t object_type); | ||
31 | DrmPropObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx = 0); | ||
32 | |||
33 | virtual ~DrmPropObject(); | ||
34 | |||
35 | private: | ||
36 | std::map<uint32_t, uint64_t> m_prop_values; | ||
37 | }; | ||
38 | } | ||
diff --git a/kms++/inc/kms++/dumbframebuffer.h b/kms++/inc/kms++/dumbframebuffer.h new file mode 100644 index 0000000..fb99d0e --- /dev/null +++ b/kms++/inc/kms++/dumbframebuffer.h | |||
@@ -0,0 +1,47 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "framebuffer.h" | ||
4 | #include "pixelformats.h" | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | |||
9 | class DumbFramebuffer : public Framebuffer | ||
10 | { | ||
11 | public: | ||
12 | DumbFramebuffer(Card& card, uint32_t width, uint32_t height, const std::string& fourcc); | ||
13 | DumbFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format); | ||
14 | virtual ~DumbFramebuffer(); | ||
15 | |||
16 | uint32_t width() const { return Framebuffer::width(); } | ||
17 | uint32_t height() const { return Framebuffer::height(); } | ||
18 | |||
19 | PixelFormat format() const { return m_format; } | ||
20 | unsigned num_planes() const { return m_num_planes; } | ||
21 | |||
22 | uint32_t handle(unsigned plane) const { return m_planes[plane].handle; } | ||
23 | uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } | ||
24 | uint32_t size(unsigned plane) const { return m_planes[plane].size; } | ||
25 | uint32_t offset(unsigned plane) const { return m_planes[plane].offset; } | ||
26 | uint8_t* map(unsigned plane); | ||
27 | int prime_fd(unsigned plane); | ||
28 | |||
29 | private: | ||
30 | struct FramebufferPlane { | ||
31 | uint32_t handle; | ||
32 | int prime_fd; | ||
33 | uint32_t size; | ||
34 | uint32_t stride; | ||
35 | uint32_t offset; | ||
36 | uint8_t *map; | ||
37 | }; | ||
38 | |||
39 | void Create(); | ||
40 | void Destroy(); | ||
41 | |||
42 | unsigned m_num_planes; | ||
43 | struct FramebufferPlane m_planes[4]; | ||
44 | |||
45 | PixelFormat m_format; | ||
46 | }; | ||
47 | } | ||
diff --git a/kms++/inc/kms++/encoder.h b/kms++/inc/kms++/encoder.h new file mode 100644 index 0000000..1d36adc --- /dev/null +++ b/kms++/inc/kms++/encoder.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <vector> | ||
4 | #include "drmpropobject.h" | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | |||
9 | struct EncoderPriv; | ||
10 | |||
11 | class Encoder : public DrmPropObject | ||
12 | { | ||
13 | friend class Card; | ||
14 | public: | ||
15 | void refresh(); | ||
16 | |||
17 | Crtc* get_crtc() const; | ||
18 | std::vector<Crtc*> get_possible_crtcs() const; | ||
19 | |||
20 | const std::string& get_encoder_type() const; | ||
21 | private: | ||
22 | Encoder(Card& card, uint32_t id, uint32_t idx); | ||
23 | ~Encoder(); | ||
24 | |||
25 | EncoderPriv* m_priv; | ||
26 | }; | ||
27 | } | ||
diff --git a/kms++/inc/kms++/extframebuffer.h b/kms++/inc/kms++/extframebuffer.h new file mode 100644 index 0000000..5f0660c --- /dev/null +++ b/kms++/inc/kms++/extframebuffer.h | |||
@@ -0,0 +1,47 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "framebuffer.h" | ||
4 | #include "pixelformats.h" | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | |||
9 | class ExtFramebuffer : public Framebuffer | ||
10 | { | ||
11 | public: | ||
12 | ExtFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format, | ||
13 | std::vector<uint32_t> handles, std::vector<uint32_t> pitches, std::vector<uint32_t> offsets); | ||
14 | ExtFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format, | ||
15 | std::vector<int> fds, std::vector<uint32_t> pitches, std::vector<uint32_t> offsets); | ||
16 | virtual ~ExtFramebuffer(); | ||
17 | |||
18 | uint32_t width() const { return Framebuffer::width(); } | ||
19 | uint32_t height() const { return Framebuffer::height(); } | ||
20 | |||
21 | PixelFormat format() const { return m_format; } | ||
22 | unsigned num_planes() const { return m_num_planes; } | ||
23 | |||
24 | uint32_t handle(unsigned plane) const { return m_planes[plane].handle; } | ||
25 | uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } | ||
26 | uint32_t size(unsigned plane) const { return m_planes[plane].size; } | ||
27 | uint32_t offset(unsigned plane) const { return m_planes[plane].offset; } | ||
28 | uint8_t* map(unsigned plane); | ||
29 | int prime_fd(unsigned plane); | ||
30 | |||
31 | private: | ||
32 | struct FramebufferPlane { | ||
33 | uint32_t handle; | ||
34 | int prime_fd; | ||
35 | uint32_t size; | ||
36 | uint32_t stride; | ||
37 | uint32_t offset; | ||
38 | uint8_t *map; | ||
39 | }; | ||
40 | |||
41 | unsigned m_num_planes; | ||
42 | struct FramebufferPlane m_planes[4]; | ||
43 | |||
44 | PixelFormat m_format; | ||
45 | }; | ||
46 | |||
47 | } | ||
diff --git a/kms++/inc/kms++/framebuffer.h b/kms++/inc/kms++/framebuffer.h new file mode 100644 index 0000000..3d43d08 --- /dev/null +++ b/kms++/inc/kms++/framebuffer.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "drmobject.h" | ||
4 | #include "pixelformats.h" | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | class IFramebuffer { | ||
9 | public: | ||
10 | virtual ~IFramebuffer() { } | ||
11 | |||
12 | virtual uint32_t width() const = 0; | ||
13 | virtual uint32_t height() const = 0; | ||
14 | |||
15 | virtual PixelFormat format() const { throw std::runtime_error("not implemented"); } | ||
16 | virtual unsigned num_planes() const { throw std::runtime_error("not implemented"); } | ||
17 | |||
18 | virtual uint32_t stride(unsigned plane) const { throw std::runtime_error("not implemented"); } | ||
19 | virtual uint32_t size(unsigned plane) const { throw std::runtime_error("not implemented"); } | ||
20 | virtual uint32_t offset(unsigned plane) const { throw std::runtime_error("not implemented"); } | ||
21 | virtual uint8_t* map(unsigned plane) { throw std::runtime_error("not implemented"); } | ||
22 | virtual int prime_fd(unsigned plane) { throw std::runtime_error("not implemented"); } | ||
23 | }; | ||
24 | |||
25 | class Framebuffer : public DrmObject, public IFramebuffer | ||
26 | { | ||
27 | public: | ||
28 | Framebuffer(Card& card, uint32_t id); | ||
29 | virtual ~Framebuffer(); | ||
30 | |||
31 | uint32_t width() const { return m_width; } | ||
32 | uint32_t height() const { return m_height; } | ||
33 | |||
34 | void flush(); | ||
35 | protected: | ||
36 | Framebuffer(Card& card, uint32_t width, uint32_t height); | ||
37 | |||
38 | private: | ||
39 | uint32_t m_width; | ||
40 | uint32_t m_height; | ||
41 | }; | ||
42 | |||
43 | } | ||
diff --git a/kms++/inc/kms++/kms++.h b/kms++/inc/kms++/kms++.h new file mode 100644 index 0000000..3365ef7 --- /dev/null +++ b/kms++/inc/kms++/kms++.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "atomicreq.h" | ||
4 | #include "card.h" | ||
5 | #include "connector.h" | ||
6 | #include "crtc.h" | ||
7 | #include "encoder.h" | ||
8 | #include "framebuffer.h" | ||
9 | #include "dumbframebuffer.h" | ||
10 | #include "extframebuffer.h" | ||
11 | #include "plane.h" | ||
12 | #include "property.h" | ||
13 | #include "blob.h" | ||
14 | #include "pipeline.h" | ||
15 | #include "pagefliphandler.h" | ||
diff --git a/kms++/inc/kms++/mode_cvt.h b/kms++/inc/kms++/mode_cvt.h new file mode 100644 index 0000000..8902e4a --- /dev/null +++ b/kms++/inc/kms++/mode_cvt.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/videomode.h> | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | |||
8 | Videomode videomode_from_cvt(uint32_t hact, uint32_t vact, uint32_t refresh, bool ilace, bool reduced_v2, bool video_optimized); | ||
9 | |||
10 | } | ||
diff --git a/kms++/inc/kms++/modedb.h b/kms++/inc/kms++/modedb.h new file mode 100644 index 0000000..b6447c6 --- /dev/null +++ b/kms++/inc/kms++/modedb.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <cstdint> | ||
4 | #include "videomode.h" | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | struct Videomode; | ||
9 | |||
10 | extern const Videomode dmt_modes[]; | ||
11 | extern const Videomode cea_modes[]; | ||
12 | |||
13 | const Videomode& find_dmt(uint32_t width, uint32_t height, float vrefresh, bool ilace); | ||
14 | const Videomode& find_cea(uint32_t width, uint32_t height, float vrefresh, bool ilace); | ||
15 | |||
16 | } | ||
diff --git a/kms++/inc/kms++/omap/omapcard.h b/kms++/inc/kms++/omap/omapcard.h new file mode 100644 index 0000000..5c2f3a5 --- /dev/null +++ b/kms++/inc/kms++/omap/omapcard.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/card.h> | ||
4 | |||
5 | struct omap_device; | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | class OmapCard : public Card | ||
10 | { | ||
11 | public: | ||
12 | OmapCard(); | ||
13 | OmapCard(const std::string& device); | ||
14 | virtual ~OmapCard(); | ||
15 | |||
16 | struct omap_device* dev() const { return m_omap_dev; } | ||
17 | |||
18 | private: | ||
19 | struct omap_device* m_omap_dev; | ||
20 | }; | ||
21 | } | ||
diff --git a/kms++/inc/kms++/omap/omapframebuffer.h b/kms++/inc/kms++/omap/omapframebuffer.h new file mode 100644 index 0000000..70bf946 --- /dev/null +++ b/kms++/inc/kms++/omap/omapframebuffer.h | |||
@@ -0,0 +1,62 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/framebuffer.h> | ||
4 | #include <kms++/pixelformats.h> | ||
5 | |||
6 | struct omap_bo; | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | class OmapCard; | ||
11 | |||
12 | class OmapFramebuffer : public Framebuffer | ||
13 | { | ||
14 | public: | ||
15 | enum Flags | ||
16 | { | ||
17 | None = 0, | ||
18 | Tiled = 1 << 0, | ||
19 | MemContig = 1 << 1, | ||
20 | MemTiler = 1 << 2, | ||
21 | MemPin = 1 << 3, | ||
22 | }; | ||
23 | |||
24 | OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, const std::string& fourcc, Flags flags = Flags::None); | ||
25 | OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, PixelFormat format, Flags flags = Flags::None); | ||
26 | virtual ~OmapFramebuffer(); | ||
27 | |||
28 | uint32_t width() const { return Framebuffer::width(); } | ||
29 | uint32_t height() const { return Framebuffer::height(); } | ||
30 | |||
31 | PixelFormat format() const { return m_format; } | ||
32 | unsigned num_planes() const { return m_num_planes; } | ||
33 | |||
34 | uint32_t handle(unsigned plane) const { return m_planes[plane].handle; } | ||
35 | uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } | ||
36 | uint32_t size(unsigned plane) const { return m_planes[plane].size; } | ||
37 | uint32_t offset(unsigned plane) const { return m_planes[plane].offset; } | ||
38 | uint8_t* map(unsigned plane); | ||
39 | int prime_fd(unsigned plane); | ||
40 | |||
41 | private: | ||
42 | OmapCard& m_omap_card; | ||
43 | |||
44 | struct FramebufferPlane { | ||
45 | struct omap_bo* omap_bo; | ||
46 | uint32_t handle; | ||
47 | int prime_fd; | ||
48 | uint32_t size; | ||
49 | uint32_t stride; | ||
50 | uint32_t offset; | ||
51 | uint8_t* map; | ||
52 | }; | ||
53 | |||
54 | void Create(Flags buffer_flags); | ||
55 | void Destroy(); | ||
56 | |||
57 | unsigned m_num_planes; | ||
58 | struct FramebufferPlane m_planes[3]; | ||
59 | |||
60 | PixelFormat m_format; | ||
61 | }; | ||
62 | } | ||
diff --git a/kms++/inc/kms++/omap/omapkms++.h b/kms++/inc/kms++/omap/omapkms++.h new file mode 100644 index 0000000..0d86841 --- /dev/null +++ b/kms++/inc/kms++/omap/omapkms++.h | |||
@@ -0,0 +1,4 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/omap/omapcard.h> | ||
4 | #include <kms++/omap/omapframebuffer.h> | ||
diff --git a/kms++/inc/kms++/pagefliphandler.h b/kms++/inc/kms++/pagefliphandler.h new file mode 100644 index 0000000..79cda0d --- /dev/null +++ b/kms++/inc/kms++/pagefliphandler.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #pragma once | ||
2 | |||
3 | namespace kms { | ||
4 | class PageFlipHandlerBase | ||
5 | { | ||
6 | public: | ||
7 | PageFlipHandlerBase() { } | ||
8 | virtual ~PageFlipHandlerBase() { } | ||
9 | virtual void handle_page_flip(uint32_t frame, double time) = 0; | ||
10 | }; | ||
11 | } | ||
diff --git a/kms++/inc/kms++/pipeline.h b/kms++/inc/kms++/pipeline.h new file mode 100644 index 0000000..ef04ec1 --- /dev/null +++ b/kms++/inc/kms++/pipeline.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "decls.h" | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | struct Pipeline { | ||
8 | Crtc* crtc; | ||
9 | Connector* connector; | ||
10 | }; | ||
11 | } | ||
diff --git a/kms++/inc/kms++/pixelformats.h b/kms++/inc/kms++/pixelformats.h new file mode 100644 index 0000000..6392de1 --- /dev/null +++ b/kms++/inc/kms++/pixelformats.h | |||
@@ -0,0 +1,67 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <cstdint> | ||
4 | #include <string> | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | constexpr uint32_t MakeFourCC(const char *fourcc) | ||
9 | { | ||
10 | return fourcc[0] | (fourcc[1] << 8) | (fourcc[2] << 16) | (fourcc[3] << 24); | ||
11 | } | ||
12 | |||
13 | enum class PixelFormat : uint32_t | ||
14 | { | ||
15 | Undefined = 0, | ||
16 | |||
17 | NV12 = MakeFourCC("NV12"), | ||
18 | NV21 = MakeFourCC("NV21"), | ||
19 | |||
20 | UYVY = MakeFourCC("UYVY"), | ||
21 | YUYV = MakeFourCC("YUYV"), | ||
22 | YVYU = MakeFourCC("YVYU"), | ||
23 | VYUY = MakeFourCC("VYUY"), | ||
24 | |||
25 | XRGB8888 = MakeFourCC("XR24"), | ||
26 | XBGR8888 = MakeFourCC("XB24"), | ||
27 | ARGB8888 = MakeFourCC("AR24"), | ||
28 | ABGR8888 = MakeFourCC("AB24"), | ||
29 | |||
30 | RGB888 = MakeFourCC("RG24"), | ||
31 | BGR888 = MakeFourCC("BG24"), | ||
32 | |||
33 | RGB565 = MakeFourCC("RG16"), | ||
34 | BGR565 = MakeFourCC("BG16"), | ||
35 | }; | ||
36 | |||
37 | static inline PixelFormat FourCCToPixelFormat(const std::string& fourcc) | ||
38 | { | ||
39 | return (PixelFormat)MakeFourCC(fourcc.c_str()); | ||
40 | } | ||
41 | |||
42 | static inline std::string PixelFormatToFourCC(PixelFormat f) | ||
43 | { | ||
44 | char buf[5] = { (char)(((int)f >> 0) & 0xff), | ||
45 | (char)(((int)f >> 8) & 0xff), | ||
46 | (char)(((int)f >> 16) & 0xff), | ||
47 | (char)(((int)f >> 24) & 0xff), | ||
48 | 0 }; | ||
49 | return std::string(buf); | ||
50 | } | ||
51 | |||
52 | struct PixelFormatPlaneInfo | ||
53 | { | ||
54 | uint8_t bitspp; | ||
55 | uint8_t xsub; | ||
56 | uint8_t ysub; | ||
57 | }; | ||
58 | |||
59 | struct PixelFormatInfo | ||
60 | { | ||
61 | uint8_t num_planes; | ||
62 | struct PixelFormatPlaneInfo planes[4]; | ||
63 | }; | ||
64 | |||
65 | const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format); | ||
66 | |||
67 | } | ||
diff --git a/kms++/inc/kms++/plane.h b/kms++/inc/kms++/plane.h new file mode 100644 index 0000000..27e819b --- /dev/null +++ b/kms++/inc/kms++/plane.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "drmpropobject.h" | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | |||
8 | enum class PlaneType | ||
9 | { | ||
10 | Overlay = 1 << 0, | ||
11 | Primary = 1 << 1, | ||
12 | Cursor = 1 << 2, | ||
13 | }; | ||
14 | |||
15 | struct PlanePriv; | ||
16 | |||
17 | class Plane : public DrmPropObject | ||
18 | { | ||
19 | friend class Card; | ||
20 | public: | ||
21 | bool supports_crtc(Crtc* crtc) const; | ||
22 | bool supports_format(PixelFormat fmt) const; | ||
23 | |||
24 | PlaneType plane_type() const; | ||
25 | |||
26 | std::vector<Crtc*> get_possible_crtcs() const; | ||
27 | std::vector<PixelFormat> get_formats() const; | ||
28 | uint32_t crtc_id() const; | ||
29 | uint32_t fb_id() const; | ||
30 | |||
31 | uint32_t crtc_x() const; | ||
32 | uint32_t crtc_y() const; | ||
33 | uint32_t x() const; | ||
34 | uint32_t y() const; | ||
35 | uint32_t gamma_size() const; | ||
36 | private: | ||
37 | Plane(Card& card, uint32_t id, uint32_t idx); | ||
38 | ~Plane(); | ||
39 | |||
40 | PlanePriv* m_priv; | ||
41 | }; | ||
42 | } | ||
diff --git a/kms++/inc/kms++/property.h b/kms++/inc/kms++/property.h new file mode 100644 index 0000000..b9097ff --- /dev/null +++ b/kms++/inc/kms++/property.h | |||
@@ -0,0 +1,44 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "drmobject.h" | ||
4 | #include <map> | ||
5 | #include <vector> | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | |||
10 | struct PropertyPriv; | ||
11 | |||
12 | enum class PropertyType | ||
13 | { | ||
14 | Range, | ||
15 | Enum, | ||
16 | Blob, | ||
17 | Bitmask, | ||
18 | Object, | ||
19 | SignedRange, | ||
20 | }; | ||
21 | |||
22 | class Property : public DrmObject | ||
23 | { | ||
24 | friend class Card; | ||
25 | public: | ||
26 | const std::string& name() const; | ||
27 | |||
28 | bool is_immutable() const; | ||
29 | bool is_pending() const; | ||
30 | |||
31 | PropertyType type() const { return m_type; } | ||
32 | std::map<uint64_t, std::string> get_enums() const; | ||
33 | std::vector<uint64_t> get_values() const; | ||
34 | std::vector<uint32_t> get_blob_ids() const; | ||
35 | private: | ||
36 | Property(Card& card, uint32_t id); | ||
37 | ~Property(); | ||
38 | |||
39 | PropertyType m_type; | ||
40 | |||
41 | PropertyPriv* m_priv; | ||
42 | std::string m_name; | ||
43 | }; | ||
44 | } | ||
diff --git a/kms++/inc/kms++/videomode.h b/kms++/inc/kms++/videomode.h new file mode 100644 index 0000000..d7f5258 --- /dev/null +++ b/kms++/inc/kms++/videomode.h | |||
@@ -0,0 +1,58 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <string> | ||
4 | #include <cstdint> | ||
5 | #include <memory> | ||
6 | |||
7 | #include "blob.h" | ||
8 | |||
9 | namespace kms | ||
10 | { | ||
11 | |||
12 | enum class SyncPolarity | ||
13 | { | ||
14 | Undefined, | ||
15 | Positive, | ||
16 | Negative, | ||
17 | }; | ||
18 | |||
19 | struct Videomode | ||
20 | { | ||
21 | std::string name; | ||
22 | |||
23 | uint32_t clock; | ||
24 | uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; | ||
25 | uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; | ||
26 | |||
27 | uint32_t vrefresh; | ||
28 | |||
29 | uint32_t flags; // DRM_MODE_FLAG_* | ||
30 | uint32_t type; // DRM_MODE_TYPE_* | ||
31 | |||
32 | std::unique_ptr<Blob> to_blob(Card& card) const; | ||
33 | |||
34 | uint16_t hfp() const { return hsync_start - hdisplay; } | ||
35 | uint16_t hsw() const { return hsync_end - hsync_start; } | ||
36 | uint16_t hbp() const { return htotal - hsync_end; } | ||
37 | |||
38 | uint16_t vfp() const { return vsync_start - vdisplay; } | ||
39 | uint16_t vsw() const { return vsync_end - vsync_start; } | ||
40 | uint16_t vbp() const { return vtotal - vsync_end; } | ||
41 | |||
42 | float calculated_vrefresh() const; | ||
43 | |||
44 | bool interlace() const; | ||
45 | SyncPolarity hsync() const; | ||
46 | SyncPolarity vsync() const; | ||
47 | |||
48 | void set_interlace(bool ilace); | ||
49 | void set_hsync(SyncPolarity pol); | ||
50 | void set_vsync(SyncPolarity pol); | ||
51 | |||
52 | std::string to_string() const; | ||
53 | }; | ||
54 | |||
55 | struct Videomode videomode_from_timings(uint32_t clock_khz, | ||
56 | uint16_t hact, uint16_t hfp, uint16_t hsw, uint16_t hbp, | ||
57 | uint16_t vact, uint16_t vfp, uint16_t vsw, uint16_t vbp); | ||
58 | } | ||
diff --git a/kms++/src/atomicreq.cpp b/kms++/src/atomicreq.cpp new file mode 100644 index 0000000..28128f2 --- /dev/null +++ b/kms++/src/atomicreq.cpp | |||
@@ -0,0 +1,117 @@ | |||
1 | #include <cassert> | ||
2 | #include <stdexcept> | ||
3 | |||
4 | #include <xf86drm.h> | ||
5 | #include <xf86drmMode.h> | ||
6 | |||
7 | #include <kms++/kms++.h> | ||
8 | |||
9 | #ifndef DRM_CLIENT_CAP_ATOMIC | ||
10 | |||
11 | #define DRM_MODE_ATOMIC_TEST_ONLY 0 | ||
12 | #define DRM_MODE_ATOMIC_NONBLOCK 0 | ||
13 | |||
14 | struct _drmModeAtomicReq; | ||
15 | typedef struct _drmModeAtomicReq* drmModeAtomicReqPtr; | ||
16 | |||
17 | static inline drmModeAtomicReqPtr drmModeAtomicAlloc() { return 0; } | ||
18 | static inline void drmModeAtomicFree(drmModeAtomicReqPtr) { } | ||
19 | static inline int drmModeAtomicAddProperty(drmModeAtomicReqPtr, uint32_t, uint32_t, uint64_t) { return 0; } | ||
20 | static inline int drmModeAtomicCommit(int, drmModeAtomicReqPtr, int, void*) { return 0; } | ||
21 | |||
22 | #endif // DRM_CLIENT_CAP_ATOMIC | ||
23 | |||
24 | using namespace std; | ||
25 | |||
26 | namespace kms | ||
27 | { | ||
28 | AtomicReq::AtomicReq(Card& card) | ||
29 | : m_card(card) | ||
30 | { | ||
31 | assert(card.has_atomic()); | ||
32 | m_req = drmModeAtomicAlloc(); | ||
33 | } | ||
34 | |||
35 | AtomicReq::~AtomicReq() | ||
36 | { | ||
37 | drmModeAtomicFree(m_req); | ||
38 | } | ||
39 | |||
40 | void AtomicReq::add(uint32_t ob_id, uint32_t prop_id, uint64_t value) | ||
41 | { | ||
42 | int r = drmModeAtomicAddProperty(m_req, ob_id, prop_id, value); | ||
43 | if (r <= 0) | ||
44 | throw std::invalid_argument("foo"); | ||
45 | } | ||
46 | |||
47 | void AtomicReq::add(DrmPropObject* ob, Property *prop, uint64_t value) | ||
48 | { | ||
49 | add(ob->id(), prop->id(), value); | ||
50 | } | ||
51 | |||
52 | void AtomicReq::add(kms::DrmPropObject* ob, const string& prop, uint64_t value) | ||
53 | { | ||
54 | add(ob, ob->get_prop(prop), value); | ||
55 | } | ||
56 | |||
57 | void AtomicReq::add(kms::DrmPropObject* ob, const map<string, uint64_t>& values) | ||
58 | { | ||
59 | for(const auto& kvp : values) | ||
60 | add(ob, kvp.first, kvp.second); | ||
61 | } | ||
62 | |||
63 | void AtomicReq::add_display(Connector* conn, Crtc* crtc, Blob* videomode, Plane* primary, Framebuffer* fb) | ||
64 | { | ||
65 | add(conn, { | ||
66 | { "CRTC_ID", crtc->id() }, | ||
67 | }); | ||
68 | |||
69 | add(crtc, { | ||
70 | { "ACTIVE", 1 }, | ||
71 | { "MODE_ID", videomode->id() }, | ||
72 | }); | ||
73 | |||
74 | add(primary, { | ||
75 | { "FB_ID", fb->id() }, | ||
76 | { "CRTC_ID", crtc->id() }, | ||
77 | { "SRC_X", 0 << 16 }, | ||
78 | { "SRC_Y", 0 << 16 }, | ||
79 | { "SRC_W", fb->width() << 16 }, | ||
80 | { "SRC_H", fb->height() << 16 }, | ||
81 | { "CRTC_X", 0 }, | ||
82 | { "CRTC_Y", 0 }, | ||
83 | { "CRTC_W", fb->width() }, | ||
84 | { "CRTC_H", fb->height() }, | ||
85 | }); | ||
86 | } | ||
87 | |||
88 | int AtomicReq::test(bool allow_modeset) | ||
89 | { | ||
90 | uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY; | ||
91 | |||
92 | if (allow_modeset) | ||
93 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | ||
94 | |||
95 | return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); | ||
96 | } | ||
97 | |||
98 | int AtomicReq::commit(void* data, bool allow_modeset) | ||
99 | { | ||
100 | uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK; | ||
101 | |||
102 | if (allow_modeset) | ||
103 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | ||
104 | |||
105 | return drmModeAtomicCommit(m_card.fd(), m_req, flags, data); | ||
106 | } | ||
107 | |||
108 | int AtomicReq::commit_sync(bool allow_modeset) | ||
109 | { | ||
110 | uint32_t flags = 0; | ||
111 | |||
112 | if (allow_modeset) | ||
113 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | ||
114 | |||
115 | return drmModeAtomicCommit(m_card.fd(), m_req, flags, 0); | ||
116 | } | ||
117 | } | ||
diff --git a/kms++/src/blob.cpp b/kms++/src/blob.cpp new file mode 100644 index 0000000..e1cd1f8 --- /dev/null +++ b/kms++/src/blob.cpp | |||
@@ -0,0 +1,51 @@ | |||
1 | #include <xf86drm.h> | ||
2 | #include <xf86drmMode.h> | ||
3 | |||
4 | #include <kms++/kms++.h> | ||
5 | |||
6 | using namespace std; | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | |||
11 | Blob::Blob(Card& card, uint32_t blob_id) | ||
12 | : DrmObject(card, blob_id, DRM_MODE_OBJECT_BLOB), m_created(false) | ||
13 | { | ||
14 | // XXX should we verify that the blob_id is a blob object? | ||
15 | } | ||
16 | |||
17 | Blob::Blob(Card& card, void* data, size_t len) | ||
18 | : DrmObject(card, DRM_MODE_OBJECT_BLOB), m_created(true) | ||
19 | { | ||
20 | uint32_t id; | ||
21 | |||
22 | int r = drmModeCreatePropertyBlob(card.fd(), data, len, &id); | ||
23 | if (r) | ||
24 | throw invalid_argument("FAILED TO CREATE PROP\n"); | ||
25 | |||
26 | set_id(id); | ||
27 | } | ||
28 | |||
29 | Blob::~Blob() | ||
30 | { | ||
31 | if (m_created) | ||
32 | drmModeDestroyPropertyBlob(card().fd(), id()); | ||
33 | } | ||
34 | |||
35 | vector<uint8_t> Blob::data() | ||
36 | { | ||
37 | drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(card().fd(), id()); | ||
38 | |||
39 | if (!blob) | ||
40 | throw invalid_argument("Blob data not available"); | ||
41 | |||
42 | uint8_t* data = (uint8_t*)blob->data; | ||
43 | |||
44 | auto v = vector<uint8_t>(data, data + blob->length); | ||
45 | |||
46 | drmModeFreePropertyBlob(blob); | ||
47 | |||
48 | return v; | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/kms++/src/card.cpp b/kms++/src/card.cpp new file mode 100644 index 0000000..f7f1a5a --- /dev/null +++ b/kms++/src/card.cpp | |||
@@ -0,0 +1,247 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <fcntl.h> | ||
4 | #include <utility> | ||
5 | #include <stdexcept> | ||
6 | #include <string.h> | ||
7 | #include <algorithm> | ||
8 | |||
9 | #include <xf86drm.h> | ||
10 | #include <xf86drmMode.h> | ||
11 | |||
12 | #include <kms++/kms++.h> | ||
13 | |||
14 | using namespace std; | ||
15 | |||
16 | namespace kms | ||
17 | { | ||
18 | |||
19 | Card::Card() | ||
20 | : Card("/dev/dri/card0") | ||
21 | { | ||
22 | } | ||
23 | |||
24 | |||
25 | Card::Card(const std::string& device) | ||
26 | { | ||
27 | int fd = open(device.c_str(), O_RDWR | O_CLOEXEC); | ||
28 | if (fd < 0) | ||
29 | throw invalid_argument(string(strerror(errno)) + " opening " + device); | ||
30 | m_fd = fd; | ||
31 | |||
32 | int r; | ||
33 | |||
34 | r = drmSetMaster(fd); | ||
35 | m_master = r == 0; | ||
36 | |||
37 | if (getenv("KMSXX_DISABLE_UNIVERSAL_PLANES") == 0) { | ||
38 | r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); | ||
39 | m_has_universal_planes = r == 0; | ||
40 | } else { | ||
41 | m_has_universal_planes = false; | ||
42 | } | ||
43 | |||
44 | #ifdef DRM_CLIENT_CAP_ATOMIC | ||
45 | if (getenv("KMSXX_DISABLE_ATOMIC") == 0) { | ||
46 | r = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); | ||
47 | m_has_atomic = r == 0; | ||
48 | } else { | ||
49 | m_has_atomic = false; | ||
50 | } | ||
51 | #else | ||
52 | m_has_atomic = false; | ||
53 | #endif | ||
54 | |||
55 | uint64_t has_dumb; | ||
56 | r = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb); | ||
57 | if (r || !has_dumb) | ||
58 | throw invalid_argument("Dumb buffers not available"); | ||
59 | |||
60 | auto res = drmModeGetResources(m_fd); | ||
61 | if (!res) | ||
62 | throw invalid_argument("Can't get card resources"); | ||
63 | |||
64 | for (int i = 0; i < res->count_connectors; ++i) { | ||
65 | uint32_t id = res->connectors[i]; | ||
66 | auto ob = new Connector(*this, id, i); | ||
67 | m_obmap[id] = ob; | ||
68 | m_connectors.push_back(ob); | ||
69 | } | ||
70 | |||
71 | for (int i = 0; i < res->count_crtcs; ++i) { | ||
72 | uint32_t id = res->crtcs[i]; | ||
73 | auto ob = new Crtc(*this, id, i); | ||
74 | m_obmap[id] = ob; | ||
75 | m_crtcs.push_back(ob); | ||
76 | } | ||
77 | |||
78 | for (int i = 0; i < res->count_encoders; ++i) { | ||
79 | uint32_t id = res->encoders[i]; | ||
80 | auto ob = new Encoder(*this, id, i); | ||
81 | m_obmap[id] = ob; | ||
82 | m_encoders.push_back(ob); | ||
83 | } | ||
84 | |||
85 | drmModeFreeResources(res); | ||
86 | |||
87 | auto planeRes = drmModeGetPlaneResources(m_fd); | ||
88 | |||
89 | for (uint i = 0; i < planeRes->count_planes; ++i) { | ||
90 | uint32_t id = planeRes->planes[i]; | ||
91 | auto ob = new Plane(*this, id, i); | ||
92 | m_obmap[id] = ob; | ||
93 | m_planes.push_back(ob); | ||
94 | } | ||
95 | |||
96 | drmModeFreePlaneResources(planeRes); | ||
97 | |||
98 | // collect all possible props | ||
99 | for (auto ob : get_objects()) { | ||
100 | auto props = drmModeObjectGetProperties(m_fd, ob->id(), ob->object_type()); | ||
101 | |||
102 | if (props == nullptr) | ||
103 | continue; | ||
104 | |||
105 | for (unsigned i = 0; i < props->count_props; ++i) { | ||
106 | uint32_t prop_id = props->props[i]; | ||
107 | |||
108 | if (m_obmap.find(prop_id) == m_obmap.end()) { | ||
109 | auto ob = new Property(*this, prop_id); | ||
110 | m_obmap[prop_id] = ob; | ||
111 | m_properties.push_back(ob); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | drmModeFreeObjectProperties(props); | ||
116 | } | ||
117 | |||
118 | for (auto pair : m_obmap) | ||
119 | pair.second->setup(); | ||
120 | } | ||
121 | |||
122 | Card::~Card() | ||
123 | { | ||
124 | restore_modes(); | ||
125 | |||
126 | while (m_framebuffers.size() > 0) | ||
127 | delete m_framebuffers.back(); | ||
128 | |||
129 | for (auto pair : m_obmap) | ||
130 | delete pair.second; | ||
131 | |||
132 | close(m_fd); | ||
133 | } | ||
134 | |||
135 | void Card::drop_master() | ||
136 | { | ||
137 | drmDropMaster(fd()); | ||
138 | } | ||
139 | |||
140 | void Card::restore_modes() | ||
141 | { | ||
142 | for (auto conn : get_connectors()) | ||
143 | conn->restore_mode(); | ||
144 | } | ||
145 | |||
146 | Connector* Card::get_first_connected_connector() const | ||
147 | { | ||
148 | for(auto c : m_connectors) { | ||
149 | if (c->connected()) | ||
150 | return c; | ||
151 | } | ||
152 | |||
153 | throw invalid_argument("no connected connectors"); | ||
154 | } | ||
155 | |||
156 | DrmObject* Card::get_object(uint32_t id) const | ||
157 | { | ||
158 | auto iter = m_obmap.find(id); | ||
159 | if (iter != m_obmap.end()) | ||
160 | return iter->second; | ||
161 | return nullptr; | ||
162 | } | ||
163 | |||
164 | const vector<DrmObject*> Card::get_objects() const | ||
165 | { | ||
166 | vector<DrmObject*> v; | ||
167 | for(auto pair : m_obmap) | ||
168 | v.push_back(pair.second); | ||
169 | return v; | ||
170 | } | ||
171 | |||
172 | Connector* Card::get_connector(uint32_t id) const { return dynamic_cast<Connector*>(get_object(id)); } | ||
173 | Crtc* Card::get_crtc(uint32_t id) const { return dynamic_cast<Crtc*>(get_object(id)); } | ||
174 | Encoder* Card::get_encoder(uint32_t id) const { return dynamic_cast<Encoder*>(get_object(id)); } | ||
175 | Property* Card::get_prop(uint32_t id) const { return dynamic_cast<Property*>(get_object(id)); } | ||
176 | Plane* Card::get_plane(uint32_t id) const { return dynamic_cast<Plane*>(get_object(id)); } | ||
177 | |||
178 | std::vector<kms::Pipeline> Card::get_connected_pipelines() | ||
179 | { | ||
180 | vector<Pipeline> outputs; | ||
181 | |||
182 | for (auto conn : get_connectors()) | ||
183 | { | ||
184 | if (conn->connected() == false) | ||
185 | continue; | ||
186 | |||
187 | Crtc* crtc = conn->get_current_crtc(); | ||
188 | |||
189 | if (!crtc) { | ||
190 | for (auto possible : conn->get_possible_crtcs()) { | ||
191 | if (find_if(outputs.begin(), outputs.end(), [possible](Pipeline out) { return out.crtc == possible; }) == outputs.end()) { | ||
192 | crtc = possible; | ||
193 | break; | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | if (!crtc) | ||
199 | throw invalid_argument(string("Connector #") + | ||
200 | to_string(conn->idx()) + | ||
201 | " has no possible crtcs"); | ||
202 | |||
203 | outputs.push_back(Pipeline { crtc, conn }); | ||
204 | } | ||
205 | |||
206 | return outputs; | ||
207 | } | ||
208 | |||
209 | static void page_flip_handler(int fd, unsigned int frame, | ||
210 | unsigned int sec, unsigned int usec, | ||
211 | void *data) | ||
212 | { | ||
213 | auto handler = (PageFlipHandlerBase*)data; | ||
214 | double time = sec + usec / 1000000.0; | ||
215 | handler->handle_page_flip(frame, time); | ||
216 | } | ||
217 | |||
218 | void Card::call_page_flip_handlers() | ||
219 | { | ||
220 | drmEventContext ev { }; | ||
221 | ev.version = DRM_EVENT_CONTEXT_VERSION; | ||
222 | ev.page_flip_handler = page_flip_handler; | ||
223 | |||
224 | drmHandleEvent(fd(), &ev); | ||
225 | } | ||
226 | |||
227 | int Card::disable_all() | ||
228 | { | ||
229 | AtomicReq req(*this); | ||
230 | |||
231 | for (Crtc* c : m_crtcs) { | ||
232 | req.add(c, { | ||
233 | { "ACTIVE", 0 }, | ||
234 | }); | ||
235 | } | ||
236 | |||
237 | for (Plane* p : m_planes) { | ||
238 | req.add(p, { | ||
239 | { "FB_ID", 0 }, | ||
240 | { "CRTC_ID", 0 }, | ||
241 | }); | ||
242 | } | ||
243 | |||
244 | return req.commit_sync(true); | ||
245 | } | ||
246 | |||
247 | } | ||
diff --git a/kms++/src/connector.cpp b/kms++/src/connector.cpp new file mode 100644 index 0000000..47759be --- /dev/null +++ b/kms++/src/connector.cpp | |||
@@ -0,0 +1,273 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <iostream> | ||
3 | #include <unistd.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <cassert> | ||
6 | #include <cmath> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | #include "helpers.h" | ||
10 | |||
11 | using namespace std; | ||
12 | |||
13 | namespace kms | ||
14 | { | ||
15 | |||
16 | #ifndef DRM_MODE_CONNECTOR_DPI | ||
17 | #define DRM_MODE_CONNECTOR_DPI 17 | ||
18 | #endif | ||
19 | |||
20 | static const map<int, string> connector_names = { | ||
21 | { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, | ||
22 | { DRM_MODE_CONNECTOR_VGA, "VGA" }, | ||
23 | { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, | ||
24 | { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, | ||
25 | { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, | ||
26 | { DRM_MODE_CONNECTOR_Composite, "Composite" }, | ||
27 | { DRM_MODE_CONNECTOR_SVIDEO, "S-Video" }, | ||
28 | { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, | ||
29 | { DRM_MODE_CONNECTOR_Component, "Component" }, | ||
30 | { DRM_MODE_CONNECTOR_9PinDIN, "9-Pin-DIN" }, | ||
31 | { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, | ||
32 | { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, | ||
33 | { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, | ||
34 | { DRM_MODE_CONNECTOR_TV, "TV" }, | ||
35 | { DRM_MODE_CONNECTOR_eDP, "eDP" }, | ||
36 | { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, | ||
37 | { DRM_MODE_CONNECTOR_DSI, "DSI" }, | ||
38 | { DRM_MODE_CONNECTOR_DPI, "DPI" }, | ||
39 | }; | ||
40 | |||
41 | static const map<int, string> connection_str = { | ||
42 | { 0, "<unknown>" }, | ||
43 | { DRM_MODE_CONNECTED, "Connected" }, | ||
44 | { DRM_MODE_DISCONNECTED, "Disconnected" }, | ||
45 | { DRM_MODE_UNKNOWNCONNECTION, "Unknown" }, | ||
46 | }; | ||
47 | |||
48 | static const map<int, string> subpix_str = { | ||
49 | #define DEF_SUBPIX(c) { DRM_MODE_SUBPIXEL_##c, #c } | ||
50 | DEF_SUBPIX(UNKNOWN), | ||
51 | DEF_SUBPIX(HORIZONTAL_RGB), | ||
52 | DEF_SUBPIX(HORIZONTAL_BGR), | ||
53 | DEF_SUBPIX(VERTICAL_RGB), | ||
54 | DEF_SUBPIX(VERTICAL_BGR), | ||
55 | DEF_SUBPIX(NONE), | ||
56 | #undef DEF_SUBPIX | ||
57 | }; | ||
58 | |||
59 | struct ConnectorPriv | ||
60 | { | ||
61 | drmModeConnectorPtr drm_connector; | ||
62 | }; | ||
63 | |||
64 | Connector::Connector(Card &card, uint32_t id, uint32_t idx) | ||
65 | :DrmPropObject(card, id, DRM_MODE_OBJECT_CONNECTOR, idx) | ||
66 | { | ||
67 | m_priv = new ConnectorPriv(); | ||
68 | |||
69 | m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id()); | ||
70 | assert(m_priv->drm_connector); | ||
71 | |||
72 | // XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id. | ||
73 | // XXX So refresh the props again here. | ||
74 | refresh_props(); | ||
75 | |||
76 | const auto& name = connector_names.at(m_priv->drm_connector->connector_type); | ||
77 | m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id); | ||
78 | } | ||
79 | |||
80 | Connector::~Connector() | ||
81 | { | ||
82 | drmModeFreeConnector(m_priv->drm_connector); | ||
83 | delete m_priv; | ||
84 | } | ||
85 | |||
86 | void Connector::refresh() | ||
87 | { | ||
88 | drmModeFreeConnector(m_priv->drm_connector); | ||
89 | |||
90 | m_priv->drm_connector = drmModeGetConnector(this->card().fd(), this->id()); | ||
91 | assert(m_priv->drm_connector); | ||
92 | |||
93 | // XXX drmModeGetConnector() does forced probe, which seems to change (at least) EDID blob id. | ||
94 | // XXX So refresh the props again here. | ||
95 | refresh_props(); | ||
96 | |||
97 | const auto& name = connector_names.at(m_priv->drm_connector->connector_type); | ||
98 | m_fullname = name + "-" + to_string(m_priv->drm_connector->connector_type_id); | ||
99 | } | ||
100 | |||
101 | void Connector::setup() | ||
102 | { | ||
103 | if (m_priv->drm_connector->encoder_id != 0) | ||
104 | m_current_encoder = card().get_encoder(m_priv->drm_connector->encoder_id); | ||
105 | else | ||
106 | m_current_encoder = 0; | ||
107 | |||
108 | if (m_current_encoder) | ||
109 | m_saved_crtc = m_current_encoder->get_crtc(); | ||
110 | else | ||
111 | m_saved_crtc = 0; | ||
112 | } | ||
113 | |||
114 | void Connector::restore_mode() | ||
115 | { | ||
116 | if (m_saved_crtc) | ||
117 | m_saved_crtc->restore_mode(this); | ||
118 | } | ||
119 | |||
120 | Videomode Connector::get_default_mode() const | ||
121 | { | ||
122 | if (m_priv->drm_connector->count_modes == 0) | ||
123 | throw invalid_argument("no modes available\n"); | ||
124 | drmModeModeInfo drmmode = m_priv->drm_connector->modes[0]; | ||
125 | |||
126 | return drm_mode_to_video_mode(drmmode); | ||
127 | } | ||
128 | |||
129 | Videomode Connector::get_mode(const string& mode) const | ||
130 | { | ||
131 | auto c = m_priv->drm_connector; | ||
132 | |||
133 | size_t idx = mode.find('@'); | ||
134 | |||
135 | string name = idx == string::npos ? mode : mode.substr(0, idx); | ||
136 | float vrefresh = idx == string::npos ? 0.0 : stod(mode.substr(idx + 1)); | ||
137 | |||
138 | for (int i = 0; i < c->count_modes; i++) { | ||
139 | Videomode m = drm_mode_to_video_mode(c->modes[i]); | ||
140 | |||
141 | if (m.name != name) | ||
142 | continue; | ||
143 | |||
144 | if (vrefresh && vrefresh != m.calculated_vrefresh()) | ||
145 | continue; | ||
146 | |||
147 | return m; | ||
148 | } | ||
149 | |||
150 | throw invalid_argument(mode + ": mode not found"); | ||
151 | } | ||
152 | |||
153 | Videomode Connector::get_mode(unsigned xres, unsigned yres, float vrefresh, bool ilace) const | ||
154 | { | ||
155 | auto c = m_priv->drm_connector; | ||
156 | |||
157 | for (int i = 0; i < c->count_modes; i++) { | ||
158 | Videomode m = drm_mode_to_video_mode(c->modes[i]); | ||
159 | |||
160 | if (m.hdisplay != xres || m.vdisplay != yres) | ||
161 | continue; | ||
162 | |||
163 | if (ilace != m.interlace()) | ||
164 | continue; | ||
165 | |||
166 | if (vrefresh && vrefresh != m.calculated_vrefresh()) | ||
167 | continue; | ||
168 | |||
169 | return m; | ||
170 | } | ||
171 | |||
172 | // If not found, do another round using rounded vrefresh | ||
173 | |||
174 | for (int i = 0; i < c->count_modes; i++) { | ||
175 | Videomode m = drm_mode_to_video_mode(c->modes[i]); | ||
176 | |||
177 | if (m.hdisplay != xres || m.vdisplay != yres) | ||
178 | continue; | ||
179 | |||
180 | if (ilace != m.interlace()) | ||
181 | continue; | ||
182 | |||
183 | if (vrefresh && vrefresh != roundf(m.calculated_vrefresh())) | ||
184 | continue; | ||
185 | |||
186 | return m; | ||
187 | } | ||
188 | |||
189 | throw invalid_argument("mode not found"); | ||
190 | } | ||
191 | |||
192 | bool Connector::connected() const | ||
193 | { | ||
194 | return m_priv->drm_connector->connection == DRM_MODE_CONNECTED || | ||
195 | m_priv->drm_connector->connection == DRM_MODE_UNKNOWNCONNECTION; | ||
196 | } | ||
197 | |||
198 | vector<Crtc*> Connector::get_possible_crtcs() const | ||
199 | { | ||
200 | vector<Crtc*> crtcs; | ||
201 | |||
202 | for (int i = 0; i < m_priv->drm_connector->count_encoders; ++i) { | ||
203 | auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]); | ||
204 | |||
205 | auto l = enc->get_possible_crtcs(); | ||
206 | |||
207 | crtcs.insert(crtcs.end(), l.begin(), l.end()); | ||
208 | } | ||
209 | |||
210 | return crtcs; | ||
211 | } | ||
212 | |||
213 | Crtc* Connector::get_current_crtc() const | ||
214 | { | ||
215 | if (m_current_encoder) | ||
216 | return m_current_encoder->get_crtc(); | ||
217 | else | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | uint32_t Connector::connector_type() const | ||
222 | { | ||
223 | return m_priv->drm_connector->connector_type; | ||
224 | } | ||
225 | |||
226 | uint32_t Connector::connector_type_id() const | ||
227 | { | ||
228 | return m_priv->drm_connector->connector_type_id; | ||
229 | } | ||
230 | |||
231 | uint32_t Connector::mmWidth() const | ||
232 | { | ||
233 | return m_priv->drm_connector->mmWidth; | ||
234 | } | ||
235 | |||
236 | uint32_t Connector::mmHeight() const | ||
237 | { | ||
238 | return m_priv->drm_connector->mmHeight; | ||
239 | } | ||
240 | |||
241 | uint32_t Connector::subpixel() const | ||
242 | { | ||
243 | return m_priv->drm_connector->subpixel; | ||
244 | } | ||
245 | |||
246 | const string& Connector::subpixel_str() const | ||
247 | { | ||
248 | return subpix_str.at(subpixel()); | ||
249 | } | ||
250 | |||
251 | std::vector<Videomode> Connector::get_modes() const | ||
252 | { | ||
253 | vector<Videomode> modes; | ||
254 | |||
255 | for (int i = 0; i < m_priv->drm_connector->count_modes; i++) | ||
256 | modes.push_back(drm_mode_to_video_mode( | ||
257 | m_priv->drm_connector->modes[i])); | ||
258 | |||
259 | return modes; | ||
260 | } | ||
261 | |||
262 | std::vector<Encoder*> Connector::get_encoders() const | ||
263 | { | ||
264 | vector<Encoder*> encoders; | ||
265 | |||
266 | for (int i = 0; i < m_priv->drm_connector->count_encoders; i++) { | ||
267 | auto enc = card().get_encoder(m_priv->drm_connector->encoders[i]); | ||
268 | encoders.push_back(enc); | ||
269 | } | ||
270 | return encoders; | ||
271 | } | ||
272 | |||
273 | } | ||
diff --git a/kms++/src/crtc.cpp b/kms++/src/crtc.cpp new file mode 100644 index 0000000..f94216f --- /dev/null +++ b/kms++/src/crtc.cpp | |||
@@ -0,0 +1,183 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <iostream> | ||
3 | #include <unistd.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <cassert> | ||
6 | |||
7 | #include <kms++/kms++.h> | ||
8 | #include "helpers.h" | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace kms | ||
13 | { | ||
14 | |||
15 | struct CrtcPriv | ||
16 | { | ||
17 | drmModeCrtcPtr drm_crtc; | ||
18 | }; | ||
19 | |||
20 | Crtc::Crtc(Card &card, uint32_t id, uint32_t idx) | ||
21 | :DrmPropObject(card, id, DRM_MODE_OBJECT_CRTC, idx) | ||
22 | { | ||
23 | m_priv = new CrtcPriv(); | ||
24 | m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id()); | ||
25 | assert(m_priv->drm_crtc); | ||
26 | } | ||
27 | |||
28 | Crtc::~Crtc() | ||
29 | { | ||
30 | drmModeFreeCrtc(m_priv->drm_crtc); | ||
31 | delete m_priv; | ||
32 | } | ||
33 | |||
34 | void Crtc::refresh() | ||
35 | { | ||
36 | drmModeFreeCrtc(m_priv->drm_crtc); | ||
37 | |||
38 | m_priv->drm_crtc = drmModeGetCrtc(this->card().fd(), this->id()); | ||
39 | assert(m_priv->drm_crtc); | ||
40 | } | ||
41 | |||
42 | void Crtc::setup() | ||
43 | { | ||
44 | for (Plane* plane : card().get_planes()) { | ||
45 | if (plane->supports_crtc(this)) | ||
46 | m_possible_planes.push_back(plane); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | void Crtc::restore_mode(Connector* conn) | ||
51 | { | ||
52 | auto c = m_priv->drm_crtc; | ||
53 | |||
54 | uint32_t conns[] = { conn->id() }; | ||
55 | |||
56 | drmModeSetCrtc(card().fd(), id(), c->buffer_id, | ||
57 | c->x, c->y, | ||
58 | conns, 1, &c->mode); | ||
59 | } | ||
60 | |||
61 | int Crtc::set_mode(Connector* conn, const Videomode& mode) | ||
62 | { | ||
63 | AtomicReq req(card()); | ||
64 | |||
65 | unique_ptr<Blob> blob = mode.to_blob(card()); | ||
66 | |||
67 | req.add(conn, { | ||
68 | { "CRTC_ID", this->id() }, | ||
69 | }); | ||
70 | |||
71 | req.add(this, { | ||
72 | { "ACTIVE", 1 }, | ||
73 | { "MODE_ID", blob->id() }, | ||
74 | }); | ||
75 | |||
76 | int r = req.commit_sync(true); | ||
77 | |||
78 | refresh(); | ||
79 | |||
80 | return r; | ||
81 | } | ||
82 | |||
83 | int Crtc::set_mode(Connector* conn, Framebuffer& fb, const Videomode& mode) | ||
84 | { | ||
85 | uint32_t conns[] = { conn->id() }; | ||
86 | drmModeModeInfo drmmode = video_mode_to_drm_mode(mode); | ||
87 | |||
88 | return drmModeSetCrtc(card().fd(), id(), fb.id(), | ||
89 | 0, 0, | ||
90 | conns, 1, &drmmode); | ||
91 | } | ||
92 | |||
93 | int Crtc::disable_mode() | ||
94 | { | ||
95 | return drmModeSetCrtc(card().fd(), id(), 0, 0, 0, 0, 0, 0); | ||
96 | } | ||
97 | |||
98 | static inline uint32_t conv(float x) | ||
99 | { | ||
100 | // XXX fix the conversion for fractional part | ||
101 | return ((uint32_t)x) << 16; | ||
102 | } | ||
103 | |||
104 | int Crtc::set_plane(Plane* plane, Framebuffer& fb, | ||
105 | int32_t dst_x, int32_t dst_y, uint32_t dst_w, uint32_t dst_h, | ||
106 | float src_x, float src_y, float src_w, float src_h) | ||
107 | { | ||
108 | return drmModeSetPlane(card().fd(), plane->id(), id(), fb.id(), 0, | ||
109 | dst_x, dst_y, dst_w, dst_h, | ||
110 | conv(src_x), conv(src_y), conv(src_w), conv(src_h)); | ||
111 | } | ||
112 | |||
113 | int Crtc::disable_plane(Plane* plane) | ||
114 | { | ||
115 | return drmModeSetPlane(card().fd(), plane->id(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | ||
116 | } | ||
117 | |||
118 | Plane* Crtc::get_primary_plane() | ||
119 | { | ||
120 | Plane *primary = nullptr; | ||
121 | |||
122 | for (Plane* p : get_possible_planes()) { | ||
123 | if (p->plane_type() != PlaneType::Primary) | ||
124 | continue; | ||
125 | |||
126 | if (p->crtc_id() == id()) | ||
127 | return p; | ||
128 | |||
129 | primary = p; | ||
130 | } | ||
131 | |||
132 | if (primary) | ||
133 | return primary; | ||
134 | |||
135 | throw invalid_argument(string("No primary plane for crtc ") + to_string(id())); | ||
136 | } | ||
137 | |||
138 | int Crtc::page_flip(Framebuffer& fb, void *data) | ||
139 | { | ||
140 | return drmModePageFlip(card().fd(), id(), fb.id(), DRM_MODE_PAGE_FLIP_EVENT, data); | ||
141 | } | ||
142 | |||
143 | uint32_t Crtc::buffer_id() const | ||
144 | { | ||
145 | return m_priv->drm_crtc->buffer_id; | ||
146 | } | ||
147 | |||
148 | uint32_t Crtc::x() const | ||
149 | { | ||
150 | return m_priv->drm_crtc->x; | ||
151 | } | ||
152 | |||
153 | uint32_t Crtc::y() const | ||
154 | { | ||
155 | return m_priv->drm_crtc->y; | ||
156 | } | ||
157 | |||
158 | uint32_t Crtc::width() const | ||
159 | { | ||
160 | return m_priv->drm_crtc->width; | ||
161 | } | ||
162 | |||
163 | uint32_t Crtc::height() const | ||
164 | { | ||
165 | return m_priv->drm_crtc->height; | ||
166 | } | ||
167 | |||
168 | int Crtc::mode_valid() const | ||
169 | { | ||
170 | return m_priv->drm_crtc->mode_valid; | ||
171 | } | ||
172 | |||
173 | Videomode Crtc::mode() const | ||
174 | { | ||
175 | return drm_mode_to_video_mode(m_priv->drm_crtc->mode); | ||
176 | } | ||
177 | |||
178 | int Crtc::gamma_size() const | ||
179 | { | ||
180 | return m_priv->drm_crtc->gamma_size; | ||
181 | } | ||
182 | |||
183 | } | ||
diff --git a/kms++/src/drmobject.cpp b/kms++/src/drmobject.cpp new file mode 100644 index 0000000..f94fc5d --- /dev/null +++ b/kms++/src/drmobject.cpp | |||
@@ -0,0 +1,34 @@ | |||
1 | #include <string.h> | ||
2 | #include <iostream> | ||
3 | #include <stdexcept> | ||
4 | |||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace kms | ||
13 | { | ||
14 | |||
15 | DrmObject::DrmObject(Card& card, uint32_t object_type) | ||
16 | :m_card(card), m_id(-1), m_object_type(object_type), m_idx(0) | ||
17 | { | ||
18 | } | ||
19 | |||
20 | DrmObject::DrmObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) | ||
21 | :m_card(card), m_id(id), m_object_type(object_type), m_idx(idx) | ||
22 | { | ||
23 | } | ||
24 | |||
25 | DrmObject::~DrmObject() | ||
26 | { | ||
27 | |||
28 | } | ||
29 | |||
30 | void DrmObject::set_id(uint32_t id) | ||
31 | { | ||
32 | m_id = id; | ||
33 | } | ||
34 | } | ||
diff --git a/kms++/src/drmpropobject.cpp b/kms++/src/drmpropobject.cpp new file mode 100644 index 0000000..f5a3c97 --- /dev/null +++ b/kms++/src/drmpropobject.cpp | |||
@@ -0,0 +1,98 @@ | |||
1 | #include <string.h> | ||
2 | #include <iostream> | ||
3 | #include <stdexcept> | ||
4 | |||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace kms | ||
13 | { | ||
14 | |||
15 | DrmPropObject::DrmPropObject(Card& card, uint32_t object_type) | ||
16 | : DrmObject(card, object_type) | ||
17 | { | ||
18 | } | ||
19 | |||
20 | DrmPropObject::DrmPropObject(Card& card, uint32_t id, uint32_t object_type, uint32_t idx) | ||
21 | : DrmObject(card, id, object_type, idx) | ||
22 | { | ||
23 | refresh_props(); | ||
24 | } | ||
25 | |||
26 | DrmPropObject::~DrmPropObject() | ||
27 | { | ||
28 | |||
29 | } | ||
30 | |||
31 | void DrmPropObject::refresh_props() | ||
32 | { | ||
33 | auto props = drmModeObjectGetProperties(card().fd(), this->id(), this->object_type()); | ||
34 | |||
35 | if (props == nullptr) | ||
36 | return; | ||
37 | |||
38 | for (unsigned i = 0; i < props->count_props; ++i) { | ||
39 | uint32_t prop_id = props->props[i]; | ||
40 | uint64_t prop_value = props->prop_values[i]; | ||
41 | |||
42 | m_prop_values[prop_id] = prop_value; | ||
43 | } | ||
44 | |||
45 | drmModeFreeObjectProperties(props); | ||
46 | } | ||
47 | |||
48 | Property* DrmPropObject::get_prop(const string& name) const | ||
49 | { | ||
50 | for (auto pair : m_prop_values) { | ||
51 | auto prop = card().get_prop(pair.first); | ||
52 | |||
53 | if (name == prop->name()) | ||
54 | return prop; | ||
55 | } | ||
56 | |||
57 | throw invalid_argument(string("property ") + name + " not found"); | ||
58 | } | ||
59 | |||
60 | uint64_t DrmPropObject::get_prop_value(uint32_t id) const | ||
61 | { | ||
62 | return m_prop_values.at(id); | ||
63 | } | ||
64 | |||
65 | uint64_t DrmPropObject::get_prop_value(const string& name) const | ||
66 | { | ||
67 | for (auto pair : m_prop_values) { | ||
68 | auto prop = card().get_prop(pair.first); | ||
69 | if (name == prop->name()) | ||
70 | return m_prop_values.at(prop->id()); | ||
71 | } | ||
72 | |||
73 | throw invalid_argument("property not found: " + name); | ||
74 | } | ||
75 | |||
76 | unique_ptr<Blob> DrmPropObject::get_prop_value_as_blob(const string& name) const | ||
77 | { | ||
78 | uint32_t blob_id = (uint32_t)get_prop_value(name); | ||
79 | |||
80 | return unique_ptr<Blob>(new Blob(card(), blob_id)); | ||
81 | } | ||
82 | |||
83 | int DrmPropObject::set_prop_value(uint32_t id, uint64_t value) | ||
84 | { | ||
85 | return drmModeObjectSetProperty(card().fd(), this->id(), this->object_type(), id, value); | ||
86 | } | ||
87 | |||
88 | int DrmPropObject::set_prop_value(const string &name, uint64_t value) | ||
89 | { | ||
90 | Property* prop = get_prop(name); | ||
91 | |||
92 | if (prop == nullptr) | ||
93 | throw invalid_argument("property not found: " + name); | ||
94 | |||
95 | return set_prop_value(prop->id(), value); | ||
96 | } | ||
97 | |||
98 | } | ||
diff --git a/kms++/src/dumbframebuffer.cpp b/kms++/src/dumbframebuffer.cpp new file mode 100644 index 0000000..b21e8ff --- /dev/null +++ b/kms++/src/dumbframebuffer.cpp | |||
@@ -0,0 +1,137 @@ | |||
1 | |||
2 | #include <cstring> | ||
3 | #include <stdexcept> | ||
4 | #include <sys/mman.h> | ||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <unistd.h> | ||
9 | #include <drm_fourcc.h> | ||
10 | #include <drm.h> | ||
11 | #include <drm_mode.h> | ||
12 | |||
13 | #include <kms++/kms++.h> | ||
14 | |||
15 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | ||
16 | |||
17 | using namespace std; | ||
18 | |||
19 | namespace kms | ||
20 | { | ||
21 | |||
22 | DumbFramebuffer::DumbFramebuffer(Card &card, uint32_t width, uint32_t height, const string& fourcc) | ||
23 | :DumbFramebuffer(card, width, height, FourCCToPixelFormat(fourcc)) | ||
24 | { | ||
25 | } | ||
26 | |||
27 | DumbFramebuffer::DumbFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format) | ||
28 | :Framebuffer(card, width, height), m_format(format) | ||
29 | { | ||
30 | Create(); | ||
31 | } | ||
32 | |||
33 | DumbFramebuffer::~DumbFramebuffer() | ||
34 | { | ||
35 | Destroy(); | ||
36 | } | ||
37 | |||
38 | void DumbFramebuffer::Create() | ||
39 | { | ||
40 | int r; | ||
41 | |||
42 | const PixelFormatInfo& format_info = get_pixel_format_info(m_format); | ||
43 | |||
44 | m_num_planes = format_info.num_planes; | ||
45 | |||
46 | for (int i = 0; i < format_info.num_planes; ++i) { | ||
47 | const PixelFormatPlaneInfo& pi = format_info.planes[i]; | ||
48 | FramebufferPlane& plane = m_planes[i]; | ||
49 | |||
50 | /* create dumb buffer */ | ||
51 | struct drm_mode_create_dumb creq = drm_mode_create_dumb(); | ||
52 | creq.width = width(); | ||
53 | creq.height = height() / pi.ysub; | ||
54 | creq.bpp = pi.bitspp; | ||
55 | r = drmIoctl(card().fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq); | ||
56 | if (r) | ||
57 | throw invalid_argument(string("DRM_IOCTL_MODE_CREATE_DUMB failed: ") + strerror(errno)); | ||
58 | |||
59 | plane.handle = creq.handle; | ||
60 | plane.stride = creq.pitch; | ||
61 | plane.size = creq.height * creq.pitch; | ||
62 | plane.offset = 0; | ||
63 | plane.map = 0; | ||
64 | plane.prime_fd = -1; | ||
65 | } | ||
66 | |||
67 | /* create framebuffer object for the dumb-buffer */ | ||
68 | uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle }; | ||
69 | uint32_t pitches[4] = { m_planes[0].stride, m_planes[1].stride }; | ||
70 | uint32_t offsets[4] = { m_planes[0].offset, m_planes[1].offset }; | ||
71 | uint32_t id; | ||
72 | r = drmModeAddFB2(card().fd(), width(), height(), (uint32_t)format(), | ||
73 | bo_handles, pitches, offsets, &id, 0); | ||
74 | if (r) | ||
75 | throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); | ||
76 | |||
77 | set_id(id); | ||
78 | } | ||
79 | |||
80 | void DumbFramebuffer::Destroy() | ||
81 | { | ||
82 | /* delete framebuffer */ | ||
83 | drmModeRmFB(card().fd(), id()); | ||
84 | |||
85 | for (uint i = 0; i < m_num_planes; ++i) { | ||
86 | FramebufferPlane& plane = m_planes[i]; | ||
87 | |||
88 | /* unmap buffer */ | ||
89 | if (plane.map) | ||
90 | munmap(plane.map, plane.size); | ||
91 | |||
92 | /* delete dumb buffer */ | ||
93 | struct drm_mode_destroy_dumb dreq = drm_mode_destroy_dumb(); | ||
94 | dreq.handle = plane.handle; | ||
95 | drmIoctl(card().fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); | ||
96 | if (plane.prime_fd >= 0) | ||
97 | ::close(plane.prime_fd); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | uint8_t* DumbFramebuffer::map(unsigned plane) | ||
102 | { | ||
103 | FramebufferPlane& p = m_planes[plane]; | ||
104 | |||
105 | if (p.map) | ||
106 | return p.map; | ||
107 | |||
108 | /* prepare buffer for memory mapping */ | ||
109 | struct drm_mode_map_dumb mreq = drm_mode_map_dumb(); | ||
110 | mreq.handle = p.handle; | ||
111 | int r = drmIoctl(card().fd(), DRM_IOCTL_MODE_MAP_DUMB, &mreq); | ||
112 | if (r) | ||
113 | throw invalid_argument(string("DRM_IOCTL_MODE_MAP_DUMB failed: ") + strerror(errno)); | ||
114 | |||
115 | /* perform actual memory mapping */ | ||
116 | p.map = (uint8_t *)mmap(0, p.size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||
117 | card().fd(), mreq.offset); | ||
118 | if (p.map == MAP_FAILED) | ||
119 | throw invalid_argument(string("mmap failed: ") + strerror(errno)); | ||
120 | |||
121 | return p.map; | ||
122 | } | ||
123 | |||
124 | int DumbFramebuffer::prime_fd(unsigned int plane) | ||
125 | { | ||
126 | if (m_planes[plane].prime_fd >= 0) | ||
127 | return m_planes[plane].prime_fd; | ||
128 | |||
129 | int r = drmPrimeHandleToFD(card().fd(), m_planes[plane].handle, | ||
130 | DRM_CLOEXEC | O_RDWR, &m_planes[plane].prime_fd); | ||
131 | if (r) | ||
132 | throw std::runtime_error("drmPrimeHandleToFD failed"); | ||
133 | |||
134 | return m_planes[plane].prime_fd; | ||
135 | } | ||
136 | |||
137 | } | ||
diff --git a/kms++/src/encoder.cpp b/kms++/src/encoder.cpp new file mode 100644 index 0000000..9cd5304 --- /dev/null +++ b/kms++/src/encoder.cpp | |||
@@ -0,0 +1,85 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <iostream> | ||
3 | #include <unistd.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <cassert> | ||
6 | #include <xf86drm.h> | ||
7 | #include <xf86drmMode.h> | ||
8 | |||
9 | #include <kms++/kms++.h> | ||
10 | |||
11 | using namespace std; | ||
12 | |||
13 | namespace kms | ||
14 | { | ||
15 | |||
16 | struct EncoderPriv | ||
17 | { | ||
18 | drmModeEncoderPtr drm_encoder; | ||
19 | }; | ||
20 | |||
21 | static const map<int, string> encoder_types = { | ||
22 | #define DEF_ENC(c) { DRM_MODE_ENCODER_##c, #c } | ||
23 | DEF_ENC(NONE), | ||
24 | DEF_ENC(DAC), | ||
25 | DEF_ENC(TMDS), | ||
26 | DEF_ENC(LVDS), | ||
27 | DEF_ENC(TVDAC), | ||
28 | DEF_ENC(VIRTUAL), | ||
29 | DEF_ENC(DSI), | ||
30 | { 7, "DPMST" }, | ||
31 | #undef DEF_ENC | ||
32 | }; | ||
33 | |||
34 | Encoder::Encoder(Card &card, uint32_t id, uint32_t idx) | ||
35 | :DrmPropObject(card, id, DRM_MODE_OBJECT_ENCODER, idx) | ||
36 | { | ||
37 | m_priv = new EncoderPriv(); | ||
38 | m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id()); | ||
39 | assert(m_priv->drm_encoder); | ||
40 | } | ||
41 | |||
42 | Encoder::~Encoder() | ||
43 | { | ||
44 | drmModeFreeEncoder(m_priv->drm_encoder); | ||
45 | delete m_priv; | ||
46 | } | ||
47 | |||
48 | void Encoder::refresh() | ||
49 | { | ||
50 | drmModeFreeEncoder(m_priv->drm_encoder); | ||
51 | |||
52 | m_priv->drm_encoder = drmModeGetEncoder(this->card().fd(), this->id()); | ||
53 | assert(m_priv->drm_encoder); | ||
54 | } | ||
55 | |||
56 | Crtc* Encoder::get_crtc() const | ||
57 | { | ||
58 | if (m_priv->drm_encoder->crtc_id) | ||
59 | return card().get_crtc(m_priv->drm_encoder->crtc_id); | ||
60 | else | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | vector<Crtc*> Encoder::get_possible_crtcs() const | ||
65 | { | ||
66 | unsigned bits = m_priv->drm_encoder->possible_crtcs; | ||
67 | vector<Crtc*> crtcs; | ||
68 | |||
69 | for (int idx = 0; bits; idx++, bits >>= 1) { | ||
70 | if ((bits & 1) == 0) | ||
71 | continue; | ||
72 | |||
73 | auto crtc = card().get_crtcs()[idx]; | ||
74 | crtcs.push_back(crtc); | ||
75 | } | ||
76 | |||
77 | return crtcs; | ||
78 | } | ||
79 | |||
80 | const string& Encoder::get_encoder_type() const | ||
81 | { | ||
82 | return encoder_types.at(m_priv->drm_encoder->encoder_type); | ||
83 | } | ||
84 | |||
85 | } | ||
diff --git a/kms++/src/extframebuffer.cpp b/kms++/src/extframebuffer.cpp new file mode 100644 index 0000000..c1f562e --- /dev/null +++ b/kms++/src/extframebuffer.cpp | |||
@@ -0,0 +1,115 @@ | |||
1 | |||
2 | #include <cstring> | ||
3 | #include <stdexcept> | ||
4 | #include <sys/mman.h> | ||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace kms | ||
13 | { | ||
14 | |||
15 | ExtFramebuffer::ExtFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format, | ||
16 | vector<uint32_t> handles, vector<uint32_t> pitches, vector<uint32_t> offsets) | ||
17 | : Framebuffer(card, width, height) | ||
18 | { | ||
19 | m_format = format; | ||
20 | |||
21 | const PixelFormatInfo& format_info = get_pixel_format_info(format); | ||
22 | |||
23 | m_num_planes = format_info.num_planes; | ||
24 | |||
25 | for (int i = 0; i < format_info.num_planes; ++i) { | ||
26 | FramebufferPlane& plane = m_planes[i]; | ||
27 | |||
28 | plane.handle = handles[i]; | ||
29 | plane.prime_fd = 0; | ||
30 | |||
31 | plane.stride = pitches[i]; | ||
32 | plane.offset = offsets[i]; | ||
33 | plane.size = plane.stride * height; | ||
34 | plane.map = 0; | ||
35 | } | ||
36 | |||
37 | uint32_t id; | ||
38 | int r = drmModeAddFB2(card.fd(), width, height, (uint32_t)format, handles.data(), pitches.data(), offsets.data(), &id, 0); | ||
39 | if (r) | ||
40 | throw std::invalid_argument(string("Failed to create ExtFramebuffer: ") + strerror(r)); | ||
41 | |||
42 | set_id(id); | ||
43 | } | ||
44 | |||
45 | ExtFramebuffer::ExtFramebuffer(Card& card, uint32_t width, uint32_t height, PixelFormat format, | ||
46 | vector<int> fds, vector<uint32_t> pitches, vector<uint32_t> offsets) | ||
47 | : Framebuffer(card, width, height) | ||
48 | { | ||
49 | int r; | ||
50 | |||
51 | m_format = format; | ||
52 | |||
53 | const PixelFormatInfo& format_info = get_pixel_format_info(format); | ||
54 | |||
55 | m_num_planes = format_info.num_planes; | ||
56 | |||
57 | for (int i = 0; i < format_info.num_planes; ++i) { | ||
58 | FramebufferPlane& plane = m_planes[i]; | ||
59 | |||
60 | plane.prime_fd = fds[i]; | ||
61 | |||
62 | r = drmPrimeFDToHandle(card.fd(), fds[i], &plane.handle); | ||
63 | if (r) | ||
64 | throw invalid_argument(string("drmPrimeFDToHandle: ") + strerror(errno)); | ||
65 | |||
66 | plane.stride = pitches[i]; | ||
67 | plane.offset = offsets[i]; | ||
68 | plane.size = plane.stride * height; | ||
69 | plane.map = 0; | ||
70 | } | ||
71 | |||
72 | uint32_t id; | ||
73 | uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle }; | ||
74 | r = drmModeAddFB2(card.fd(), width, height, (uint32_t)format, | ||
75 | bo_handles, pitches.data(), offsets.data(), &id, 0); | ||
76 | if (r) | ||
77 | throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); | ||
78 | |||
79 | set_id(id); | ||
80 | } | ||
81 | |||
82 | ExtFramebuffer::~ExtFramebuffer() | ||
83 | { | ||
84 | drmModeRmFB(card().fd(), id()); | ||
85 | } | ||
86 | |||
87 | uint8_t* ExtFramebuffer::map(unsigned plane) | ||
88 | { | ||
89 | FramebufferPlane& p = m_planes[plane]; | ||
90 | |||
91 | if (!p.prime_fd) | ||
92 | throw invalid_argument("cannot mmap non-dmabuf fb"); | ||
93 | |||
94 | if (p.map) | ||
95 | return p.map; | ||
96 | |||
97 | p.map = (uint8_t *)mmap(0, p.size, PROT_READ | PROT_WRITE, MAP_SHARED, | ||
98 | p.prime_fd, 0); | ||
99 | if (p.map == MAP_FAILED) | ||
100 | throw invalid_argument(string("mmap failed: ") + strerror(errno)); | ||
101 | |||
102 | return p.map; | ||
103 | } | ||
104 | |||
105 | int ExtFramebuffer::prime_fd(unsigned plane) | ||
106 | { | ||
107 | FramebufferPlane& p = m_planes[plane]; | ||
108 | |||
109 | if (!p.prime_fd) | ||
110 | throw invalid_argument("no primefb for non-dmabuf fb"); | ||
111 | |||
112 | return p.prime_fd; | ||
113 | } | ||
114 | |||
115 | } | ||
diff --git a/kms++/src/framebuffer.cpp b/kms++/src/framebuffer.cpp new file mode 100644 index 0000000..39c4e16 --- /dev/null +++ b/kms++/src/framebuffer.cpp | |||
@@ -0,0 +1,56 @@ | |||
1 | #include <algorithm> | ||
2 | #include <cstring> | ||
3 | #include <stdexcept> | ||
4 | #include <sys/mman.h> | ||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace kms | ||
13 | { | ||
14 | |||
15 | Framebuffer::Framebuffer(Card& card, uint32_t width, uint32_t height) | ||
16 | : DrmObject(card, DRM_MODE_OBJECT_FB), m_width(width), m_height(height) | ||
17 | { | ||
18 | card.m_framebuffers.push_back(this); | ||
19 | } | ||
20 | |||
21 | Framebuffer::Framebuffer(Card& card, uint32_t id) | ||
22 | : DrmObject(card, id, DRM_MODE_OBJECT_FB) | ||
23 | { | ||
24 | auto fb = drmModeGetFB(card.fd(), id); | ||
25 | |||
26 | if (fb) { | ||
27 | m_width = fb->width; | ||
28 | m_height = fb->height; | ||
29 | |||
30 | drmModeFreeFB(fb); | ||
31 | } else { | ||
32 | m_width = m_height = 0; | ||
33 | } | ||
34 | |||
35 | card.m_framebuffers.push_back(this); | ||
36 | } | ||
37 | |||
38 | void Framebuffer::flush() | ||
39 | { | ||
40 | drmModeClip clip { }; | ||
41 | clip.x1 = clip.y1 = 0; | ||
42 | clip.x2 = width(); | ||
43 | clip.y2 = height(); | ||
44 | |||
45 | drmModeDirtyFB(card().fd(), id(), &clip, 1); | ||
46 | } | ||
47 | |||
48 | Framebuffer::~Framebuffer() | ||
49 | { | ||
50 | auto& fbs = card().m_framebuffers; | ||
51 | auto iter = find(fbs.begin(), fbs.end(), this); | ||
52 | card().m_framebuffers.erase(iter); | ||
53 | } | ||
54 | |||
55 | |||
56 | } | ||
diff --git a/kms++/src/helpers.cpp b/kms++/src/helpers.cpp new file mode 100644 index 0000000..8bd082b --- /dev/null +++ b/kms++/src/helpers.cpp | |||
@@ -0,0 +1,72 @@ | |||
1 | |||
2 | #include <kms++/kms++.h> | ||
3 | #include "helpers.h" | ||
4 | #include <cstring> | ||
5 | |||
6 | #define CPY(field) dst.field = src.field | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode) | ||
11 | { | ||
12 | Videomode mode = { }; | ||
13 | |||
14 | auto& src = drmmode; | ||
15 | auto& dst = mode; | ||
16 | |||
17 | CPY(clock); | ||
18 | |||
19 | CPY(hdisplay); | ||
20 | CPY(hsync_start); | ||
21 | CPY(hsync_end); | ||
22 | CPY(htotal); | ||
23 | CPY(hskew); | ||
24 | |||
25 | CPY(vdisplay); | ||
26 | CPY(vsync_start); | ||
27 | CPY(vsync_end); | ||
28 | CPY(vtotal); | ||
29 | CPY(vscan); | ||
30 | |||
31 | CPY(vrefresh); | ||
32 | |||
33 | CPY(flags); | ||
34 | CPY(type); | ||
35 | |||
36 | mode.name = drmmode.name; | ||
37 | |||
38 | return mode; | ||
39 | } | ||
40 | |||
41 | drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode) | ||
42 | { | ||
43 | drmModeModeInfo drmmode = { }; | ||
44 | |||
45 | auto& src = mode; | ||
46 | auto& dst = drmmode; | ||
47 | |||
48 | CPY(clock); | ||
49 | |||
50 | CPY(hdisplay); | ||
51 | CPY(hsync_start); | ||
52 | CPY(hsync_end); | ||
53 | CPY(htotal); | ||
54 | CPY(hskew); | ||
55 | |||
56 | CPY(vdisplay); | ||
57 | CPY(vsync_start); | ||
58 | CPY(vsync_end); | ||
59 | CPY(vtotal); | ||
60 | CPY(vscan); | ||
61 | |||
62 | CPY(vrefresh); | ||
63 | |||
64 | CPY(flags); | ||
65 | CPY(type); | ||
66 | |||
67 | strncpy(drmmode.name, mode.name.c_str(), sizeof(drmmode.name)); | ||
68 | drmmode.name[sizeof(drmmode.name) - 1] = 0; | ||
69 | |||
70 | return drmmode; | ||
71 | } | ||
72 | } | ||
diff --git a/kms++/src/helpers.h b/kms++/src/helpers.h new file mode 100644 index 0000000..4eb597c --- /dev/null +++ b/kms++/src/helpers.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <xf86drm.h> | ||
4 | #include <xf86drmMode.h> | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | struct Videomode; | ||
9 | |||
10 | Videomode drm_mode_to_video_mode(const drmModeModeInfo& drmmode); | ||
11 | drmModeModeInfo video_mode_to_drm_mode(const Videomode& mode); | ||
12 | } | ||
diff --git a/kms++/src/mode_cvt.cpp b/kms++/src/mode_cvt.cpp new file mode 100644 index 0000000..41503c7 --- /dev/null +++ b/kms++/src/mode_cvt.cpp | |||
@@ -0,0 +1,154 @@ | |||
1 | // Supports CVT 1.2 reduced blanking modes v1 and v2 | ||
2 | |||
3 | #include <kms++/kms++.h> | ||
4 | #include <cmath> | ||
5 | |||
6 | using namespace std; | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | |||
11 | static float CELL_GRAN = 8; | ||
12 | static float CELL_GRAN_RND = round(CELL_GRAN); | ||
13 | |||
14 | struct CVTConsts | ||
15 | { | ||
16 | float CLOCK_STEP; | ||
17 | float MIN_V_BPORCH; | ||
18 | float RB_H_BLANK; | ||
19 | float RB_H_FPORCH; | ||
20 | float RB_H_SYNC; | ||
21 | float RB_H_BPORCH; | ||
22 | float RB_MIN_V_BLANK; | ||
23 | float RB_V_FPORCH; | ||
24 | float REFRESH_MULTIPLIER; | ||
25 | }; | ||
26 | |||
27 | static const CVTConsts cvt_consts_v1 = | ||
28 | { | ||
29 | .CLOCK_STEP = 0.25, // Fixed | ||
30 | .MIN_V_BPORCH = 6, // Min | ||
31 | .RB_H_BLANK = 160, // Fixed | ||
32 | .RB_H_FPORCH = 48, // Fixed | ||
33 | .RB_H_SYNC = 32, // Fixed | ||
34 | .RB_H_BPORCH = 80, // Fixed | ||
35 | .RB_MIN_V_BLANK = 460, // Min | ||
36 | .RB_V_FPORCH = 3, // Fixed | ||
37 | .REFRESH_MULTIPLIER = 1,// Fixed | ||
38 | }; | ||
39 | |||
40 | static const CVTConsts cvt_consts_v2 = | ||
41 | { | ||
42 | .CLOCK_STEP = 0.001, // Fixed | ||
43 | .MIN_V_BPORCH = 6, // Fixed | ||
44 | .RB_H_BLANK = 80, // Fixed | ||
45 | .RB_H_FPORCH = 8, // Fixed | ||
46 | .RB_H_SYNC = 32, // Fixed | ||
47 | .RB_H_BPORCH = 40, // Fixed | ||
48 | .RB_MIN_V_BLANK = 460, // Min | ||
49 | .RB_V_FPORCH = 1, // Min | ||
50 | .REFRESH_MULTIPLIER = 1,// or 1000/1001 | ||
51 | }; | ||
52 | |||
53 | Videomode videomode_from_cvt(uint32_t hact, uint32_t vact, uint32_t refresh, bool ilace, bool reduced_v2, bool video_optimized) | ||
54 | { | ||
55 | CVTConsts c = reduced_v2 ? cvt_consts_v2 : cvt_consts_v1; | ||
56 | |||
57 | if (video_optimized) | ||
58 | c.REFRESH_MULTIPLIER = 1000.0/1001.0; | ||
59 | |||
60 | bool INT_RQD = ilace; | ||
61 | |||
62 | float H_PIXELS = hact; | ||
63 | float V_LINES = vact; | ||
64 | float IP_FREQ_RQD = refresh ? refresh : 60; | ||
65 | if (ilace) | ||
66 | IP_FREQ_RQD /= 2; | ||
67 | |||
68 | float V_SYNC_RND; | ||
69 | |||
70 | if (reduced_v2) { | ||
71 | V_SYNC_RND = 8; | ||
72 | } else { | ||
73 | if (hact * 3 == vact * 4) | ||
74 | V_SYNC_RND = 4; | ||
75 | else if (hact * 9 == vact * 16) | ||
76 | V_SYNC_RND = 5; | ||
77 | else if (hact * 10 == vact * 16) | ||
78 | V_SYNC_RND = 6; | ||
79 | else if (hact == 1280 && (vact == 1024 || vact == 768)) | ||
80 | V_SYNC_RND = 7; | ||
81 | else | ||
82 | V_SYNC_RND = 10; | ||
83 | } | ||
84 | |||
85 | // 5.2.1 | ||
86 | float V_FIELD_RATE_RQD = INT_RQD ? IP_FREQ_RQD * 2 : IP_FREQ_RQD; | ||
87 | |||
88 | // 5.2.2 | ||
89 | float H_PIXELS_RND = floor(H_PIXELS / CELL_GRAN_RND) * CELL_GRAN_RND; | ||
90 | |||
91 | // 5.2.3 | ||
92 | float LEFT_MARGIN = 0; | ||
93 | float RIGHT_MARGIN = 0; | ||
94 | |||
95 | // 5.2.4 | ||
96 | float TOTAL_ACTIVE_PIXELS = H_PIXELS_RND + LEFT_MARGIN + RIGHT_MARGIN; | ||
97 | |||
98 | // 5.2.5 | ||
99 | float V_LINES_RND = INT_RQD ? floor(V_LINES / 2) : floor(V_LINES); | ||
100 | |||
101 | // 5.2.6 | ||
102 | float TOP_MARGIN = 0; | ||
103 | float BOT_MARGIN = 0; | ||
104 | |||
105 | // 5.2.7 | ||
106 | float INTERLACE = INT_RQD ? 0.5 : 0; | ||
107 | |||
108 | // 5.4.8 | ||
109 | float H_PERIOD_EST = ((1000000 / V_FIELD_RATE_RQD) - c.RB_MIN_V_BLANK) / (V_LINES_RND + TOP_MARGIN + BOT_MARGIN); | ||
110 | |||
111 | // 5.4.9 | ||
112 | float VBI_LINES = floor(c.RB_MIN_V_BLANK / H_PERIOD_EST) + 1; | ||
113 | |||
114 | // 5.4.10 | ||
115 | float RB_MIN_VBI = c.RB_V_FPORCH + V_SYNC_RND + c.MIN_V_BPORCH; | ||
116 | float ACT_VBI_LINES = VBI_LINES < RB_MIN_VBI ? RB_MIN_VBI : VBI_LINES; | ||
117 | |||
118 | // 5.4.11 | ||
119 | float TOTAL_V_LINES = ACT_VBI_LINES + V_LINES_RND + TOP_MARGIN + BOT_MARGIN + INTERLACE; | ||
120 | |||
121 | // 5.4.12 | ||
122 | float TOTAL_PIXELS = c.RB_H_BLANK + TOTAL_ACTIVE_PIXELS; | ||
123 | |||
124 | // 5.4.13 | ||
125 | float ACT_PIXEL_FREQ = c.CLOCK_STEP * floor((V_FIELD_RATE_RQD * TOTAL_V_LINES * TOTAL_PIXELS / 1000000 * c.REFRESH_MULTIPLIER) / c.CLOCK_STEP); | ||
126 | |||
127 | // 5.4.14 | ||
128 | //float ACT_H_FREQ = 1000 * ACT_PIXEL_FREQ / TOTAL_PIXELS; | ||
129 | |||
130 | // 5.4.15 | ||
131 | //float ACT_FIELD_RATE = 1000 * ACT_H_FREQ / TOTAL_V_LINES; | ||
132 | |||
133 | // 5.4.16 | ||
134 | //float ACT_FRAME_RATE = INT_RQD ? ACT_FIELD_RATE / 2 : ACT_FIELD_RATE; | ||
135 | |||
136 | // 3.4.3.7 Adjust vfp | ||
137 | if (reduced_v2) | ||
138 | c.RB_V_FPORCH = ACT_VBI_LINES - V_SYNC_RND - c.MIN_V_BPORCH; | ||
139 | |||
140 | Videomode mode; | ||
141 | |||
142 | mode = videomode_from_timings(ACT_PIXEL_FREQ * 1000, | ||
143 | H_PIXELS_RND, c.RB_H_FPORCH, c.RB_H_SYNC, c.RB_H_BPORCH, | ||
144 | V_LINES_RND * (INT_RQD ? 2 : 1), c.RB_V_FPORCH, V_SYNC_RND, ACT_VBI_LINES - V_SYNC_RND - c.RB_V_FPORCH); | ||
145 | |||
146 | mode.set_hsync(SyncPolarity::Positive); | ||
147 | mode.set_vsync(SyncPolarity::Negative); | ||
148 | |||
149 | mode.set_interlace(INT_RQD); | ||
150 | |||
151 | return mode; | ||
152 | } | ||
153 | |||
154 | } | ||
diff --git a/kms++/src/modedb.cpp b/kms++/src/modedb.cpp new file mode 100644 index 0000000..5d5d373 --- /dev/null +++ b/kms++/src/modedb.cpp | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <xf86drm.h> | ||
2 | #include <stdexcept> | ||
3 | #include <cmath> | ||
4 | |||
5 | #include <kms++/modedb.h> | ||
6 | |||
7 | using namespace std; | ||
8 | |||
9 | namespace kms | ||
10 | { | ||
11 | |||
12 | static const Videomode& find_from_table(const Videomode* modes, uint32_t width, uint32_t height, float vrefresh, bool ilace) | ||
13 | { | ||
14 | for (unsigned i = 0; modes[i].clock; ++i) { | ||
15 | const Videomode& m = modes[i]; | ||
16 | |||
17 | if (m.hdisplay != width || m.vdisplay != height) | ||
18 | continue; | ||
19 | |||
20 | if (ilace != m.interlace()) | ||
21 | continue; | ||
22 | |||
23 | if (vrefresh && vrefresh != m.calculated_vrefresh()) | ||
24 | continue; | ||
25 | |||
26 | return m; | ||
27 | } | ||
28 | |||
29 | // If not found, do another round using rounded vrefresh | ||
30 | |||
31 | for (unsigned i = 0; modes[i].clock; ++i) { | ||
32 | const Videomode& m = modes[i]; | ||
33 | |||
34 | if (m.hdisplay != width || m.vdisplay != height) | ||
35 | continue; | ||
36 | |||
37 | if (ilace != m.interlace()) | ||
38 | continue; | ||
39 | |||
40 | if (vrefresh && vrefresh != roundf(m.calculated_vrefresh())) | ||
41 | continue; | ||
42 | |||
43 | return m; | ||
44 | } | ||
45 | |||
46 | throw invalid_argument("mode not found"); | ||
47 | } | ||
48 | |||
49 | const Videomode& find_dmt(uint32_t width, uint32_t height, float vrefresh, bool ilace) | ||
50 | { | ||
51 | return find_from_table(dmt_modes, width, height, vrefresh, ilace); | ||
52 | } | ||
53 | |||
54 | const Videomode& find_cea(uint32_t width, uint32_t height, float vrefresh, bool ilace) | ||
55 | { | ||
56 | return find_from_table(cea_modes, width, height, vrefresh, ilace); | ||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/kms++/src/modedb_cea.cpp b/kms++/src/modedb_cea.cpp new file mode 100644 index 0000000..a99a612 --- /dev/null +++ b/kms++/src/modedb_cea.cpp | |||
@@ -0,0 +1,402 @@ | |||
1 | /* From Linux kernel: drm_edid.c */ | ||
2 | /* | ||
3 | * Copyright (c) 2006 Luc Verhaegen (quirks list) | ||
4 | * Copyright (c) 2007-2008 Intel Corporation | ||
5 | * Jesse Barnes <jesse.barnes@intel.com> | ||
6 | * Copyright 2010 Red Hat, Inc. | ||
7 | * | ||
8 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from | ||
9 | * FB layer. | ||
10 | * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> | ||
11 | * | ||
12 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
13 | * copy of this software and associated documentation files (the "Software"), | ||
14 | * to deal in the Software without restriction, including without limitation | ||
15 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | ||
16 | * and/or sell copies of the Software, and to permit persons to whom the | ||
17 | * Software is furnished to do so, subject to the following conditions: | ||
18 | * | ||
19 | * The above copyright notice and this permission notice (including the | ||
20 | * next paragraph) shall be included in all copies or substantial portions | ||
21 | * of the Software. | ||
22 | * | ||
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
26 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
28 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
29 | * DEALINGS IN THE SOFTWARE. | ||
30 | */ | ||
31 | |||
32 | #include <kms++/modedb.h> | ||
33 | |||
34 | #include <xf86drm.h> | ||
35 | |||
36 | namespace kms | ||
37 | { | ||
38 | |||
39 | #define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) | ||
40 | |||
41 | #define DRM_MODE(nm, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ | ||
42 | .name = nm, .clock = (c), \ | ||
43 | .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), .htotal = (ht), .hskew = (hsk), \ | ||
44 | .vdisplay = (vd), .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), .vscan = (vs), \ | ||
45 | .vrefresh = DIV_ROUND(c * 1000, ht * vt) * (((f) & DRM_MODE_FLAG_INTERLACE) ? 2 : 1), \ | ||
46 | .flags = (f), .type = 0 | ||
47 | |||
48 | /* | ||
49 | * Probably taken from CEA-861 spec. | ||
50 | * This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c. | ||
51 | */ | ||
52 | const Videomode cea_modes[] = { | ||
53 | /* 1 - 640x480@60Hz */ | ||
54 | { DRM_MODE("640x480", 25175, 640, 656, | ||
55 | 752, 800, 0, 480, 490, 492, 525, 0, | ||
56 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
57 | }, | ||
58 | /* 2 - 720x480@60Hz */ | ||
59 | { DRM_MODE("720x480", 27000, 720, 736, | ||
60 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
61 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
62 | }, | ||
63 | /* 3 - 720x480@60Hz */ | ||
64 | { DRM_MODE("720x480", 27000, 720, 736, | ||
65 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
66 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
67 | }, | ||
68 | /* 4 - 1280x720@60Hz */ | ||
69 | { DRM_MODE("1280x720", 74250, 1280, 1390, | ||
70 | 1430, 1650, 0, 720, 725, 730, 750, 0, | ||
71 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
72 | }, | ||
73 | /* 5 - 1920x1080i@60Hz */ | ||
74 | { DRM_MODE("1920x1080i", 74250, 1920, 2008, | ||
75 | 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, | ||
76 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | | ||
77 | DRM_MODE_FLAG_INTERLACE), | ||
78 | }, | ||
79 | /* 6 - 720(1440)x480i@60Hz */ | ||
80 | { DRM_MODE("720x480i", 13500, 720, 739, | ||
81 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
82 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
83 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
84 | }, | ||
85 | /* 7 - 720(1440)x480i@60Hz */ | ||
86 | { DRM_MODE("720x480i", 13500, 720, 739, | ||
87 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
88 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
89 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
90 | }, | ||
91 | /* 8 - 720(1440)x240@60Hz */ | ||
92 | { DRM_MODE("720x240", 13500, 720, 739, | ||
93 | 801, 858, 0, 240, 244, 247, 262, 0, | ||
94 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
95 | DRM_MODE_FLAG_DBLCLK), | ||
96 | }, | ||
97 | /* 9 - 720(1440)x240@60Hz */ | ||
98 | { DRM_MODE("720x240", 13500, 720, 739, | ||
99 | 801, 858, 0, 240, 244, 247, 262, 0, | ||
100 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
101 | DRM_MODE_FLAG_DBLCLK), | ||
102 | }, | ||
103 | /* 10 - 2880x480i@60Hz */ | ||
104 | { DRM_MODE("2880x480i", 54000, 2880, 2956, | ||
105 | 3204, 3432, 0, 480, 488, 494, 525, 0, | ||
106 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
107 | DRM_MODE_FLAG_INTERLACE), | ||
108 | }, | ||
109 | /* 11 - 2880x480i@60Hz */ | ||
110 | { DRM_MODE("2880x480i", 54000, 2880, 2956, | ||
111 | 3204, 3432, 0, 480, 488, 494, 525, 0, | ||
112 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
113 | DRM_MODE_FLAG_INTERLACE), | ||
114 | }, | ||
115 | /* 12 - 2880x240@60Hz */ | ||
116 | { DRM_MODE("2880x240", 54000, 2880, 2956, | ||
117 | 3204, 3432, 0, 240, 244, 247, 262, 0, | ||
118 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
119 | }, | ||
120 | /* 13 - 2880x240@60Hz */ | ||
121 | { DRM_MODE("2880x240", 54000, 2880, 2956, | ||
122 | 3204, 3432, 0, 240, 244, 247, 262, 0, | ||
123 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
124 | }, | ||
125 | /* 14 - 1440x480@60Hz */ | ||
126 | { DRM_MODE("1440x480", 54000, 1440, 1472, | ||
127 | 1596, 1716, 0, 480, 489, 495, 525, 0, | ||
128 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
129 | }, | ||
130 | /* 15 - 1440x480@60Hz */ | ||
131 | { DRM_MODE("1440x480", 54000, 1440, 1472, | ||
132 | 1596, 1716, 0, 480, 489, 495, 525, 0, | ||
133 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
134 | }, | ||
135 | /* 16 - 1920x1080@60Hz */ | ||
136 | { DRM_MODE("1920x1080", 148500, 1920, 2008, | ||
137 | 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, | ||
138 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
139 | }, | ||
140 | /* 17 - 720x576@50Hz */ | ||
141 | { DRM_MODE("720x576", 27000, 720, 732, | ||
142 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
143 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
144 | }, | ||
145 | /* 18 - 720x576@50Hz */ | ||
146 | { DRM_MODE("720x576", 27000, 720, 732, | ||
147 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
148 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
149 | }, | ||
150 | /* 19 - 1280x720@50Hz */ | ||
151 | { DRM_MODE("1280x720", 74250, 1280, 1720, | ||
152 | 1760, 1980, 0, 720, 725, 730, 750, 0, | ||
153 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
154 | }, | ||
155 | /* 20 - 1920x1080i@50Hz */ | ||
156 | { DRM_MODE("1920x1080i", 74250, 1920, 2448, | ||
157 | 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, | ||
158 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | | ||
159 | DRM_MODE_FLAG_INTERLACE), | ||
160 | }, | ||
161 | /* 21 - 720(1440)x576i@50Hz */ | ||
162 | { DRM_MODE("720x576i", 13500, 720, 732, | ||
163 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
164 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
165 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
166 | }, | ||
167 | /* 22 - 720(1440)x576i@50Hz */ | ||
168 | { DRM_MODE("720x576i", 13500, 720, 732, | ||
169 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
170 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
171 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
172 | }, | ||
173 | /* 23 - 720(1440)x288@50Hz */ | ||
174 | { DRM_MODE("720x288", 13500, 720, 732, | ||
175 | 795, 864, 0, 288, 290, 293, 312, 0, | ||
176 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
177 | DRM_MODE_FLAG_DBLCLK), | ||
178 | }, | ||
179 | /* 24 - 720(1440)x288@50Hz */ | ||
180 | { DRM_MODE("720x288", 13500, 720, 732, | ||
181 | 795, 864, 0, 288, 290, 293, 312, 0, | ||
182 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
183 | DRM_MODE_FLAG_DBLCLK), | ||
184 | }, | ||
185 | /* 25 - 2880x576i@50Hz */ | ||
186 | { DRM_MODE("2880x576i", 54000, 2880, 2928, | ||
187 | 3180, 3456, 0, 576, 580, 586, 625, 0, | ||
188 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
189 | DRM_MODE_FLAG_INTERLACE), | ||
190 | }, | ||
191 | /* 26 - 2880x576i@50Hz */ | ||
192 | { DRM_MODE("2880x576i", 54000, 2880, 2928, | ||
193 | 3180, 3456, 0, 576, 580, 586, 625, 0, | ||
194 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
195 | DRM_MODE_FLAG_INTERLACE), | ||
196 | }, | ||
197 | /* 27 - 2880x288@50Hz */ | ||
198 | { DRM_MODE("2880x288", 54000, 2880, 2928, | ||
199 | 3180, 3456, 0, 288, 290, 293, 312, 0, | ||
200 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
201 | }, | ||
202 | /* 28 - 2880x288@50Hz */ | ||
203 | { DRM_MODE("2880x288", 54000, 2880, 2928, | ||
204 | 3180, 3456, 0, 288, 290, 293, 312, 0, | ||
205 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
206 | }, | ||
207 | /* 29 - 1440x576@50Hz */ | ||
208 | { DRM_MODE("1440x576", 54000, 1440, 1464, | ||
209 | 1592, 1728, 0, 576, 581, 586, 625, 0, | ||
210 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
211 | }, | ||
212 | /* 30 - 1440x576@50Hz */ | ||
213 | { DRM_MODE("1440x576", 54000, 1440, 1464, | ||
214 | 1592, 1728, 0, 576, 581, 586, 625, 0, | ||
215 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
216 | }, | ||
217 | /* 31 - 1920x1080@50Hz */ | ||
218 | { DRM_MODE("1920x1080", 148500, 1920, 2448, | ||
219 | 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, | ||
220 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
221 | }, | ||
222 | /* 32 - 1920x1080@24Hz */ | ||
223 | { DRM_MODE("1920x1080", 74250, 1920, 2558, | ||
224 | 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, | ||
225 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
226 | }, | ||
227 | /* 33 - 1920x1080@25Hz */ | ||
228 | { DRM_MODE("1920x1080", 74250, 1920, 2448, | ||
229 | 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, | ||
230 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
231 | }, | ||
232 | /* 34 - 1920x1080@30Hz */ | ||
233 | { DRM_MODE("1920x1080", 74250, 1920, 2008, | ||
234 | 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, | ||
235 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
236 | }, | ||
237 | /* 35 - 2880x480@60Hz */ | ||
238 | { DRM_MODE("2880x480", 108000, 2880, 2944, | ||
239 | 3192, 3432, 0, 480, 489, 495, 525, 0, | ||
240 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
241 | }, | ||
242 | /* 36 - 2880x480@60Hz */ | ||
243 | { DRM_MODE("2880x480", 108000, 2880, 2944, | ||
244 | 3192, 3432, 0, 480, 489, 495, 525, 0, | ||
245 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
246 | }, | ||
247 | /* 37 - 2880x576@50Hz */ | ||
248 | { DRM_MODE("2880x576", 108000, 2880, 2928, | ||
249 | 3184, 3456, 0, 576, 581, 586, 625, 0, | ||
250 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
251 | }, | ||
252 | /* 38 - 2880x576@50Hz */ | ||
253 | { DRM_MODE("2880x576", 108000, 2880, 2928, | ||
254 | 3184, 3456, 0, 576, 581, 586, 625, 0, | ||
255 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
256 | }, | ||
257 | /* 39 - 1920x1080i@50Hz */ | ||
258 | { DRM_MODE("1920x1080i", 72000, 1920, 1952, | ||
259 | 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, | ||
260 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
261 | DRM_MODE_FLAG_INTERLACE), | ||
262 | }, | ||
263 | /* 40 - 1920x1080i@100Hz */ | ||
264 | { DRM_MODE("1920x1080i", 148500, 1920, 2448, | ||
265 | 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, | ||
266 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | | ||
267 | DRM_MODE_FLAG_INTERLACE), | ||
268 | }, | ||
269 | /* 41 - 1280x720@100Hz */ | ||
270 | { DRM_MODE("1280x720", 148500, 1280, 1720, | ||
271 | 1760, 1980, 0, 720, 725, 730, 750, 0, | ||
272 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
273 | }, | ||
274 | /* 42 - 720x576@100Hz */ | ||
275 | { DRM_MODE("720x576", 54000, 720, 732, | ||
276 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
277 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
278 | }, | ||
279 | /* 43 - 720x576@100Hz */ | ||
280 | { DRM_MODE("720x576", 54000, 720, 732, | ||
281 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
282 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
283 | }, | ||
284 | /* 44 - 720(1440)x576i@100Hz */ | ||
285 | { DRM_MODE("720x576i", 27000, 720, 732, | ||
286 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
287 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
288 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
289 | }, | ||
290 | /* 45 - 720(1440)x576i@100Hz */ | ||
291 | { DRM_MODE("720x576i", 27000, 720, 732, | ||
292 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
293 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
294 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
295 | }, | ||
296 | /* 46 - 1920x1080i@120Hz */ | ||
297 | { DRM_MODE("1920x1080i", 148500, 1920, 2008, | ||
298 | 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, | ||
299 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | | ||
300 | DRM_MODE_FLAG_INTERLACE), | ||
301 | }, | ||
302 | /* 47 - 1280x720@120Hz */ | ||
303 | { DRM_MODE("1280x720", 148500, 1280, 1390, | ||
304 | 1430, 1650, 0, 720, 725, 730, 750, 0, | ||
305 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
306 | }, | ||
307 | /* 48 - 720x480@120Hz */ | ||
308 | { DRM_MODE("720x480", 54000, 720, 736, | ||
309 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
310 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
311 | }, | ||
312 | /* 49 - 720x480@120Hz */ | ||
313 | { DRM_MODE("720x480", 54000, 720, 736, | ||
314 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
315 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
316 | }, | ||
317 | /* 50 - 720(1440)x480i@120Hz */ | ||
318 | { DRM_MODE("720x480i", 27000, 720, 739, | ||
319 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
320 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
321 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
322 | }, | ||
323 | /* 51 - 720(1440)x480i@120Hz */ | ||
324 | { DRM_MODE("720x480i", 27000, 720, 739, | ||
325 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
326 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
327 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
328 | }, | ||
329 | /* 52 - 720x576@200Hz */ | ||
330 | { DRM_MODE("720x576", 108000, 720, 732, | ||
331 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
332 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
333 | }, | ||
334 | /* 53 - 720x576@200Hz */ | ||
335 | { DRM_MODE("720x576", 108000, 720, 732, | ||
336 | 796, 864, 0, 576, 581, 586, 625, 0, | ||
337 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
338 | }, | ||
339 | /* 54 - 720(1440)x576i@200Hz */ | ||
340 | { DRM_MODE("720x576i", 54000, 720, 732, | ||
341 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
342 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
343 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
344 | }, | ||
345 | /* 55 - 720(1440)x576i@200Hz */ | ||
346 | { DRM_MODE("720x576i", 54000, 720, 732, | ||
347 | 795, 864, 0, 576, 580, 586, 625, 0, | ||
348 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
349 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
350 | }, | ||
351 | /* 56 - 720x480@240Hz */ | ||
352 | { DRM_MODE("720x480", 108000, 720, 736, | ||
353 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
354 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
355 | }, | ||
356 | /* 57 - 720x480@240Hz */ | ||
357 | { DRM_MODE("720x480", 108000, 720, 736, | ||
358 | 798, 858, 0, 480, 489, 495, 525, 0, | ||
359 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
360 | }, | ||
361 | /* 58 - 720(1440)x480i@240 */ | ||
362 | { DRM_MODE("720x480i", 54000, 720, 739, | ||
363 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
364 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
365 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
366 | }, | ||
367 | /* 59 - 720(1440)x480i@240 */ | ||
368 | { DRM_MODE("720x480i", 54000, 720, 739, | ||
369 | 801, 858, 0, 480, 488, 494, 525, 0, | ||
370 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | ||
371 | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), | ||
372 | }, | ||
373 | /* 60 - 1280x720@24Hz */ | ||
374 | { DRM_MODE("1280x720", 59400, 1280, 3040, | ||
375 | 3080, 3300, 0, 720, 725, 730, 750, 0, | ||
376 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
377 | }, | ||
378 | /* 61 - 1280x720@25Hz */ | ||
379 | { DRM_MODE("1280x720", 74250, 1280, 3700, | ||
380 | 3740, 3960, 0, 720, 725, 730, 750, 0, | ||
381 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
382 | }, | ||
383 | /* 62 - 1280x720@30Hz */ | ||
384 | { DRM_MODE("1280x720", 74250, 1280, 3040, | ||
385 | 3080, 3300, 0, 720, 725, 730, 750, 0, | ||
386 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
387 | }, | ||
388 | /* 63 - 1920x1080@120Hz */ | ||
389 | { DRM_MODE("1920x1080", 297000, 1920, 2008, | ||
390 | 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, | ||
391 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
392 | }, | ||
393 | /* 64 - 1920x1080@100Hz */ | ||
394 | { DRM_MODE("1920x1080", 297000, 1920, 2448, | ||
395 | 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, | ||
396 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
397 | }, | ||
398 | /* TERMINATOR */ | ||
399 | { }, | ||
400 | }; | ||
401 | |||
402 | } | ||
diff --git a/kms++/src/modedb_dmt.cpp b/kms++/src/modedb_dmt.cpp new file mode 100644 index 0000000..7dee6b1 --- /dev/null +++ b/kms++/src/modedb_dmt.cpp | |||
@@ -0,0 +1,204 @@ | |||
1 | /* Generated from DMTr1 v13.pdf */ | ||
2 | |||
3 | #include <kms++/modedb.h> | ||
4 | |||
5 | #include <xf86drm.h> | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | |||
10 | #define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) | ||
11 | |||
12 | // hd, hss, hse, ht, vd, vss, vse, vt | ||
13 | |||
14 | #define DRM_MODE(nm, c, hact, hfp, hsw, hbp, vact, vfp, vsw, vbp, f) \ | ||
15 | { \ | ||
16 | .name = nm, .clock = c, \ | ||
17 | .hdisplay = (hact), .hsync_start = (hact) + (hfp), .hsync_end = (hact) + (hfp) + (hsw), .htotal = (hact) + (hfp) + (hsw) + (hbp), .hskew = 0, \ | ||
18 | .vdisplay = (vact), .vsync_start = (vact) + (vfp), .vsync_end = (vact) + (vfp) + (vsw), .vtotal = (vact) + (vfp) + (vsw) + (vbp), .vscan = 0, \ | ||
19 | .vrefresh = DIV_ROUND(c * 1000, ((hact) + (hfp) + (hsw) + (hbp)) * ((vact) + (vfp) + (vsw) + (vbp))) * (((f) & DRM_MODE_FLAG_INTERLACE) ? 2 : 1), \ | ||
20 | .flags = (f), .type = 0 \ | ||
21 | } | ||
22 | |||
23 | const Videomode dmt_modes[] = { | ||
24 | // 0x1 - 640 x 350 @ 85Hz | ||
25 | DRM_MODE("640 x 350 @ 85Hz", 31500, 640, 32, 64, 96, 350, 32, 3, 60, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
26 | // 0x2 - 640 x 400 @ 85Hz | ||
27 | DRM_MODE("640 x 400 @ 85Hz", 31500, 640, 32, 64, 96, 400, 1, 3, 41, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
28 | // 0x3 - 720 x 400 @ 85Hz | ||
29 | DRM_MODE("720 x 400 @ 85Hz", 35500, 720, 36, 72, 108, 400, 1, 3, 42, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
30 | // 0x4 - 640 x 480 @ 60Hz | ||
31 | DRM_MODE("640 x 480 @ 60Hz", 25175, 640, 8, 96, 40, 480, 2, 2, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
32 | // 0x5 - 640 x 480 @ 72Hz | ||
33 | DRM_MODE("640 x 480 @ 72Hz", 31500, 640, 16, 40, 120, 480, 1, 3, 20, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
34 | // 0x6 - 640 x 480 @ 75Hz | ||
35 | DRM_MODE("640 x 480 @ 75Hz", 31500, 640, 16, 64, 120, 480, 1, 3, 16, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
36 | // 0x7 - 640 x 480 @ 85Hz | ||
37 | DRM_MODE("640 x 480 @ 85Hz", 36000, 640, 56, 56, 80, 480, 1, 3, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
38 | // 0x8 - 800 x 600 @ 56Hz | ||
39 | DRM_MODE("800 x 600 @ 56Hz", 36000, 800, 24, 72, 128, 600, 1, 2, 22, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
40 | // 0x9 - 800 x 600 @ 60Hz | ||
41 | DRM_MODE("800 x 600 @ 60Hz", 40000, 800, 40, 128, 88, 600, 1, 4, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
42 | // 0xa - 800 x 600 @ 72Hz | ||
43 | DRM_MODE("800 x 600 @ 72Hz", 50000, 800, 56, 120, 64, 600, 37, 6, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
44 | // 0xb - 800 x 600 @ 75Hz | ||
45 | DRM_MODE("800 x 600 @ 75Hz", 49500, 800, 16, 80, 160, 600, 1, 3, 21, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
46 | // 0xc - 800 x 600 @ 85Hz | ||
47 | DRM_MODE("800 x 600 @ 85Hz", 56250, 800, 32, 64, 152, 600, 1, 3, 27, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
48 | // 0xd - 800 x 600 @ 120Hz CVT (Reduced Blanking) | ||
49 | DRM_MODE("800 x 600 @ 120Hz CVT (Reduced Blanking)", 73250, 800, 48, 32, 80, 600, 3, 4, 29, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
50 | // 0xe - 848 x 480 @ 60Hz | ||
51 | DRM_MODE("848 x 480 @ 60Hz", 33750, 848, 16, 112, 112, 480, 6, 8, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
52 | // 0xf - 1024 x 768 @ 43Hz (Interlaced) | ||
53 | DRM_MODE("1024 x 768 @ 43Hz (Interlaced)", 44900, 1024, 8, 176, 56, 768, 0, 4, 20, DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
54 | // 0x10 - 1024 x 768 @ 60Hz | ||
55 | DRM_MODE("1024 x 768 @ 60Hz", 65000, 1024, 24, 136, 160, 768, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
56 | // 0x11 - 1024 x 768 @ 70Hz | ||
57 | DRM_MODE("1024 x 768 @ 70Hz", 75000, 1024, 24, 136, 144, 768, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
58 | // 0x12 - 1024 x 768 @ 75Hz | ||
59 | DRM_MODE("1024 x 768 @ 75Hz", 78750, 1024, 16, 96, 176, 768, 1, 3, 28, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
60 | // 0x13 - 1024 x 768 @ 85Hz | ||
61 | DRM_MODE("1024 x 768 @ 85Hz", 94500, 1024, 48, 96, 208, 768, 1, 3, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
62 | // 0x14 - 1024 x 768 @ 120Hz CVT (Reduced Blanking) | ||
63 | DRM_MODE("1024 x 768 @ 120Hz CVT (Reduced Blanking)", 115500, 1024, 48, 32, 80, 768, 3, 4, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
64 | // 0x15 - 1152 x 864 @ 75Hz | ||
65 | DRM_MODE("1152 x 864 @ 75Hz", 108000, 1152, 64, 128, 256, 864, 1, 3, 32, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
66 | // 0x55 - 1280 x 720 @ 60Hz | ||
67 | DRM_MODE("1280 x 720 @ 60Hz", 74250, 1280, 110, 40, 220, 720, 5, 5, 20, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
68 | // 0x16 - 1280 x 768 @ 60Hz CVT (Reduced Blanking) | ||
69 | DRM_MODE("1280 x 768 @ 60Hz CVT (Reduced Blanking)", 68250, 1280, 48, 32, 80, 768, 3, 7, 12, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
70 | // 0x17 - 1280 x 768 @ 60Hz | ||
71 | DRM_MODE("1280 x 768 @ 60Hz", 79500, 1280, 64, 128, 192, 768, 3, 7, 20, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
72 | // 0x18 - 1280 x 768 @ 75Hz | ||
73 | DRM_MODE("1280 x 768 @ 75Hz", 102250, 1280, 80, 128, 208, 768, 3, 7, 27, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
74 | // 0x19 - 1280 x 768 @ 85Hz | ||
75 | DRM_MODE("1280 x 768 @ 85Hz", 117500, 1280, 80, 136, 216, 768, 3, 7, 31, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
76 | // 0x1a - 1280 x 768 @ 120Hz CVT (Reduced Blanking) | ||
77 | DRM_MODE("1280 x 768 @ 120Hz CVT (Reduced Blanking)", 140250, 1280, 48, 32, 80, 768, 3, 7, 35, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
78 | // 0x1b - 1280 x 800 @ 60Hz CVT (Reduced Blanking) | ||
79 | DRM_MODE("1280 x 800 @ 60Hz CVT (Reduced Blanking)", 71000, 1280, 48, 32, 80, 800, 3, 6, 14, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
80 | // 0x1c - 1280 x 800 @ 60Hz | ||
81 | DRM_MODE("1280 x 800 @ 60Hz", 83500, 1280, 72, 128, 200, 800, 3, 6, 22, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
82 | // 0x1d - 1280 x 800 @ 75Hz | ||
83 | DRM_MODE("1280 x 800 @ 75Hz", 106500, 1280, 80, 128, 208, 800, 3, 6, 29, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
84 | // 0x1e - 1280 x 800 @ 85Hz | ||
85 | DRM_MODE("1280 x 800 @ 85Hz", 122500, 1280, 80, 136, 216, 800, 3, 6, 34, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
86 | // 0x1f - 1280 x 800 @ 120Hz CVT (Reduced Blanking) | ||
87 | DRM_MODE("1280 x 800 @ 120Hz CVT (Reduced Blanking)", 146250, 1280, 48, 32, 80, 800, 3, 6, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
88 | // 0x20 - 1280 x 960 @ 60Hz | ||
89 | DRM_MODE("1280 x 960 @ 60Hz", 108000, 1280, 96, 112, 312, 960, 1, 3, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
90 | // 0x21 - 1280 x 960 @ 85Hz | ||
91 | DRM_MODE("1280 x 960 @ 85Hz", 148500, 1280, 64, 160, 224, 960, 1, 3, 47, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
92 | // 0x22 - 1280 x 960 @ 120Hz CVT (Reduced Blanking) | ||
93 | DRM_MODE("1280 x 960 @ 120Hz CVT (Reduced Blanking)", 175500, 1280, 48, 32, 80, 960, 3, 4, 50, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
94 | // 0x23 - 1280 x 1024 @ 60Hz | ||
95 | DRM_MODE("1280 x 1024 @ 60Hz", 108000, 1280, 48, 112, 248, 1024, 1, 3, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
96 | // 0x24 - 1280 x 1024 @ 75Hz | ||
97 | DRM_MODE("1280 x 1024 @ 75Hz", 135000, 1280, 16, 144, 248, 1024, 1, 3, 38, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
98 | // 0x25 - 1280 x 1024 @ 85Hz | ||
99 | DRM_MODE("1280 x 1024 @ 85Hz", 157500, 1280, 64, 160, 224, 1024, 1, 3, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
100 | // 0x26 - 1280 x 1024 @ 120Hz CVT (Reduced Blanking) | ||
101 | DRM_MODE("1280 x 1024 @ 120Hz CVT (Reduced Blanking)", 187250, 1280, 48, 32, 80, 1024, 3, 7, 50, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
102 | // 0x27 - 1360 x 768 @ 60Hz | ||
103 | DRM_MODE("1360 x 768 @ 60Hz", 85500, 1360, 64, 112, 256, 768, 3, 6, 18, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
104 | // 0x28 - 1360 x 768 @ 120Hz CVT (Reduced Blanking) | ||
105 | DRM_MODE("1360 x 768 @ 120Hz CVT (Reduced Blanking)", 148250, 1360, 48, 32, 80, 768, 3, 5, 37, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
106 | // 0x51 - 1366 x 768 @ 60Hz | ||
107 | DRM_MODE("1366 x 768 @ 60Hz", 85500, 1366, 70, 143, 213, 768, 3, 3, 24, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
108 | // 0x56 - 1366 x 768 @ 60Hz | ||
109 | DRM_MODE("1366 x 768 @ 60Hz", 72000, 1366, 14, 56, 64, 768, 1, 3, 28, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
110 | // 0x29 - 1400 x 1050 @ 60Hz CVT (Reduced Blanking) | ||
111 | DRM_MODE("1400 x 1050 @ 60Hz CVT (Reduced Blanking)", 101000, 1400, 48, 32, 80, 1050, 3, 4, 23, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
112 | // 0x2a - 1400 x 1050 @ 60Hz | ||
113 | DRM_MODE("1400 x 1050 @ 60Hz", 121750, 1400, 88, 144, 232, 1050, 3, 4, 32, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
114 | // 0x2b - 1400 x 1050 @ 75Hz | ||
115 | DRM_MODE("1400 x 1050 @ 75Hz", 156000, 1400, 104, 144, 248, 1050, 3, 4, 42, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
116 | // 0x2c - 1400 x 1050 @ 85Hz | ||
117 | DRM_MODE("1400 x 1050 @ 85Hz", 179500, 1400, 104, 152, 256, 1050, 3, 4, 48, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
118 | // 0x2d - 1400 x 1050 @ 120Hz CVT (Reduced Blanking) | ||
119 | DRM_MODE("1400 x 1050 @ 120Hz CVT (Reduced Blanking)", 208000, 1400, 48, 32, 80, 1050, 3, 4, 55, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
120 | // 0x2e - 1440 x 900 @ 60Hz CVT (Reduced Blanking) | ||
121 | DRM_MODE("1440 x 900 @ 60Hz CVT (Reduced Blanking)", 88750, 1440, 48, 32, 80, 900, 3, 6, 17, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
122 | // 0x2f - 1440 x 900 @ 60Hz | ||
123 | DRM_MODE("1440 x 900 @ 60Hz", 106500, 1440, 80, 152, 232, 900, 3, 6, 25, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
124 | // 0x30 - 1440 x 900 @ 75Hz | ||
125 | DRM_MODE("1440 x 900 @ 75Hz", 136750, 1440, 96, 152, 248, 900, 3, 6, 33, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
126 | // 0x31 - 1440 x 900 @ 85Hz | ||
127 | DRM_MODE("1440 x 900 @ 85Hz", 157000, 1440, 104, 152, 256, 900, 3, 6, 39, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
128 | // 0x32 - 1440 x 900 @ 120Hz CVT (Reduced Blanking) | ||
129 | DRM_MODE("1440 x 900 @ 120Hz CVT (Reduced Blanking)", 182750, 1440, 48, 32, 80, 900, 3, 6, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
130 | // 0x53 - 1600 x 900 @ 60Hz | ||
131 | DRM_MODE("1600 x 900 @ 60Hz", 108000, 1600, 24, 80, 96, 900, 1, 3, 96, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
132 | // 0x33 - 1600 x 1200 @ 60Hz | ||
133 | DRM_MODE("1600 x 1200 @ 60Hz", 162000, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
134 | // 0x34 - 1600 x 1200 @ 65Hz | ||
135 | DRM_MODE("1600 x 1200 @ 65Hz", 175500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
136 | // 0x35 - 1600 x 1200 @ 70Hz | ||
137 | DRM_MODE("1600 x 1200 @ 70Hz", 189000, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
138 | // 0x36 - 1600 x 1200 @ 75Hz | ||
139 | DRM_MODE("1600 x 1200 @ 75Hz", 202500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
140 | // 0x37 - 1600 x 1200 @ 85Hz | ||
141 | DRM_MODE("1600 x 1200 @ 85Hz", 229500, 1600, 64, 192, 304, 1200, 1, 3, 46, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
142 | // 0x38 - 1600 x 1200 @ 120Hz CVT (Reduced Blanking) | ||
143 | DRM_MODE("1600 x 1200 @ 120Hz CVT (Reduced Blanking)", 268250, 1600, 48, 32, 80, 1200, 3, 4, 64, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
144 | // 0x39 - 1680 x 1050 @ 60Hz CVT (Reduced Blanking) | ||
145 | DRM_MODE("1680 x 1050 @ 60Hz CVT (Reduced Blanking)", 119000, 1680, 48, 32, 80, 1050, 3, 6, 21, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
146 | // 0x3a - 1680 x 1050 @ 60Hz | ||
147 | DRM_MODE("1680 x 1050 @ 60Hz", 146250, 1680, 104, 176, 280, 1050, 3, 6, 30, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
148 | // 0x3b - 1680 x 1050 @ 75Hz | ||
149 | DRM_MODE("1680 x 1050 @ 75Hz", 187000, 1680, 120, 176, 296, 1050, 3, 6, 40, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
150 | // 0x3c - 1680 x 1050 @ 85Hz | ||
151 | DRM_MODE("1680 x 1050 @ 85Hz", 214750, 1680, 128, 176, 304, 1050, 3, 6, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
152 | // 0x3d - 1680 x 1050 @ 120Hz CVT (Reduced Blanking) | ||
153 | DRM_MODE("1680 x 1050 @ 120Hz CVT (Reduced Blanking)", 245500, 1680, 48, 32, 80, 1050, 3, 6, 53, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
154 | // 0x3e - 1792 x 1344 @ 60 Hz | ||
155 | DRM_MODE("1792 x 1344 @ 60 Hz", 204750, 1792, 128, 200, 328, 1344, 1, 3, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
156 | // 0x3f - 1792 x 1344 @ 75Hz | ||
157 | DRM_MODE("1792 x 1344 @ 75Hz", 261000, 1792, 96, 216, 352, 1344, 1, 3, 69, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
158 | // 0x40 - 1792 x 1344 @ 120Hz CVT (Reduced Blanking) | ||
159 | DRM_MODE("1792 x 1344 @ 120Hz CVT (Reduced Blanking)", 333250, 1792, 48, 32, 80, 1344, 3, 4, 72, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
160 | // 0x41 - 1856 x 1392 at 60Hz | ||
161 | DRM_MODE("1856 x 1392 at 60Hz", 218250, 1856, 96, 224, 352, 1392, 1, 3, 43, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
162 | // 0x42 - 1856 x 1392 @ 75Hz | ||
163 | DRM_MODE("1856 x 1392 @ 75Hz", 288000, 1856, 128, 224, 352, 1392, 1, 3, 104, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
164 | // 0x43 - 1856 x 1392 @ 120Hz CVT (Reduced Blanking) | ||
165 | DRM_MODE("1856 x 1392 @ 120Hz CVT (Reduced Blanking)", 356500, 1856, 48, 32, 80, 1392, 3, 4, 75, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
166 | // 0x52 - 1920 x 1080 @ 60Hz | ||
167 | DRM_MODE("1920 x 1080 @ 60Hz", 148500, 1920, 88, 44, 148, 1080, 4, 5, 36, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
168 | // 0x44 - 1920 x 1200 @ 60Hz CVT (Reduced Blanking) | ||
169 | DRM_MODE("1920 x 1200 @ 60Hz CVT (Reduced Blanking)", 154000, 1920, 48, 32, 80, 1200, 3, 6, 26, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
170 | // 0x45 - 1920 x 1200 @ 60Hz | ||
171 | DRM_MODE("1920 x 1200 @ 60Hz", 193250, 1920, 136, 200, 336, 1200, 3, 6, 36, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
172 | // 0x46 - 1920 x 1200 @ 75Hz | ||
173 | DRM_MODE("1920 x 1200 @ 75Hz", 245250, 1920, 136, 208, 344, 1200, 3, 6, 46, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
174 | // 0x47 - 1920 x 1200 @ 85Hz | ||
175 | DRM_MODE("1920 x 1200 @ 85Hz", 281250, 1920, 144, 208, 352, 1200, 3, 6, 53, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
176 | // 0x48 - 1920 x 1200 @ 120Hz CVT (Reduced Blanking) | ||
177 | DRM_MODE("1920 x 1200 @ 120Hz CVT (Reduced Blanking)", 317000, 1920, 48, 32, 80, 1200, 3, 6, 62, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
178 | // 0x49 - 1920 x 1440 @ 60Hz | ||
179 | DRM_MODE("1920 x 1440 @ 60Hz", 234000, 1920, 128, 208, 344, 1440, 1, 3, 56, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
180 | // 0x4a - 1920 x 1440 @ 75Hz | ||
181 | DRM_MODE("1920 x 1440 @ 75Hz", 297000, 1920, 144, 224, 352, 1440, 1, 3, 56, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
182 | // 0x4b - 1920 x 1440 @ 120Hz CVT (Reduced Blanking) | ||
183 | DRM_MODE("1920 x 1440 @ 120Hz CVT (Reduced Blanking)", 380500, 1920, 48, 32, 80, 1440, 3, 4, 78, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
184 | // 0x54 - 2048 x 1152 @ 60Hz | ||
185 | DRM_MODE("2048 x 1152 @ 60Hz", 162000, 2048, 26, 80, 96, 1152, 1, 3, 44, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
186 | // 0x4c - 2560 x 1600 @ 60Hz CVT (Reduced Blanking) | ||
187 | DRM_MODE("2560 x 1600 @ 60Hz CVT (Reduced Blanking)", 268500, 2560, 48, 32, 80, 1600, 3, 6, 37, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
188 | // 0x4d - 2560 x 1600 @ 60Hz | ||
189 | DRM_MODE("2560 x 1600 @ 60Hz", 348500, 2560, 192, 280, 472, 1600, 3, 6, 49, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
190 | // 0x4e - 2560 x 1600 @ 75Hz | ||
191 | DRM_MODE("2560 x 1600 @ 75Hz", 443250, 2560, 208, 280, 488, 1600, 3, 6, 63, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
192 | // 0x4f - 2560 x 1600 @ 85Hz | ||
193 | DRM_MODE("2560 x 1600 @ 85Hz", 505250, 2560, 208, 280, 488, 1600, 3, 6, 73, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
194 | // 0x50 - 2560 x 1600 @ 120Hz CVT (Reduced Blanking) | ||
195 | DRM_MODE("2560 x 1600 @ 120Hz CVT (Reduced Blanking)", 552750, 2560, 48, 32, 80, 1600, 3, 6, 85, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
196 | // 0x57 - 4096 x 2160 @ 60Hz CVT (Reduced Blanking v2) | ||
197 | DRM_MODE("4096 x 2160 @ 60Hz CVT (Reduced Blanking v2)", 556744, 4096, 8, 32, 40, 2160, 48, 8, 6, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
198 | // 0x58 - 4096 x 2160 @ 59.94 Hz CVT (Reduced Blanking v2) | ||
199 | DRM_MODE("4096 x 2160 @ 59.94 Hz CVT (Reduced Blanking v2)", 556188, 4096, 8, 32, 40, 2160, 48, 8, 6, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC), | ||
200 | /* TERMINATOR */ | ||
201 | { }, | ||
202 | }; | ||
203 | |||
204 | } | ||
diff --git a/kms++/src/omap/omapcard.cpp b/kms++/src/omap/omapcard.cpp new file mode 100644 index 0000000..e811b6d --- /dev/null +++ b/kms++/src/omap/omapcard.cpp | |||
@@ -0,0 +1,29 @@ | |||
1 | |||
2 | #include <kms++/omap/omapcard.h> | ||
3 | |||
4 | extern "C" { | ||
5 | #include <omap_drmif.h> | ||
6 | } | ||
7 | |||
8 | using namespace std; | ||
9 | |||
10 | namespace kms | ||
11 | { | ||
12 | OmapCard::OmapCard() | ||
13 | : OmapCard("/dev/dri/card0") | ||
14 | { | ||
15 | |||
16 | } | ||
17 | |||
18 | OmapCard::OmapCard(const string& device) | ||
19 | : Card(device) | ||
20 | { | ||
21 | m_omap_dev = omap_device_new(fd()); | ||
22 | } | ||
23 | |||
24 | OmapCard::~OmapCard() | ||
25 | { | ||
26 | omap_device_del(m_omap_dev); | ||
27 | } | ||
28 | |||
29 | } | ||
diff --git a/kms++/src/omap/omapframebuffer.cpp b/kms++/src/omap/omapframebuffer.cpp new file mode 100644 index 0000000..f33a286 --- /dev/null +++ b/kms++/src/omap/omapframebuffer.cpp | |||
@@ -0,0 +1,184 @@ | |||
1 | |||
2 | #include <cstring> | ||
3 | #include <stdexcept> | ||
4 | #include <sys/mman.h> | ||
5 | #include <xf86drm.h> | ||
6 | #include <xf86drmMode.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <unistd.h> | ||
9 | #include <drm_fourcc.h> | ||
10 | #include <drm.h> | ||
11 | #include <drm_mode.h> | ||
12 | |||
13 | #include <kms++/kms++.h> | ||
14 | #include <kms++/omap/omapkms++.h> | ||
15 | |||
16 | extern "C" { | ||
17 | #include <omap_drmif.h> | ||
18 | } | ||
19 | |||
20 | #define __round_mask(x, y) ((__typeof__(x))((y)-1)) | ||
21 | #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) | ||
22 | #define PAGE_SIZE 4096 | ||
23 | |||
24 | using namespace std; | ||
25 | |||
26 | namespace kms | ||
27 | { | ||
28 | |||
29 | OmapFramebuffer::OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, const string& fourcc, Flags flags) | ||
30 | : OmapFramebuffer(card, width, height, FourCCToPixelFormat(fourcc), flags) | ||
31 | { | ||
32 | } | ||
33 | |||
34 | OmapFramebuffer::OmapFramebuffer(OmapCard& card, uint32_t width, uint32_t height, PixelFormat format, Flags flags) | ||
35 | :Framebuffer(card, width, height), m_omap_card(card), m_format(format) | ||
36 | { | ||
37 | Create(flags); | ||
38 | } | ||
39 | |||
40 | OmapFramebuffer::~OmapFramebuffer() | ||
41 | { | ||
42 | Destroy(); | ||
43 | } | ||
44 | |||
45 | void OmapFramebuffer::Create(Flags buffer_flags) | ||
46 | { | ||
47 | const PixelFormatInfo& format_info = get_pixel_format_info(m_format); | ||
48 | |||
49 | m_num_planes = format_info.num_planes; | ||
50 | |||
51 | for (int i = 0; i < format_info.num_planes; ++i) { | ||
52 | const PixelFormatPlaneInfo& pi = format_info.planes[i]; | ||
53 | FramebufferPlane& plane = m_planes[i]; | ||
54 | |||
55 | uint32_t flags = OMAP_BO_SCANOUT | OMAP_BO_WC; | ||
56 | |||
57 | #if defined(OMAP_BO_MEM_CONTIG) | ||
58 | if (buffer_flags & Flags::MemContig) | ||
59 | flags |= OMAP_BO_MEM_CONTIG; | ||
60 | if (buffer_flags & Flags::MemTiler) | ||
61 | flags |= OMAP_BO_MEM_TILER; | ||
62 | if (buffer_flags & Flags::MemPin) | ||
63 | flags |= OMAP_BO_MEM_PIN; | ||
64 | #endif | ||
65 | |||
66 | struct omap_bo* bo; | ||
67 | |||
68 | uint32_t stride; | ||
69 | |||
70 | if (!(buffer_flags & Flags::Tiled)) { | ||
71 | stride = width() * pi.bitspp / 8; | ||
72 | |||
73 | uint32_t size = stride * height() / pi.ysub; | ||
74 | |||
75 | bo = omap_bo_new(m_omap_card.dev(), size, flags); | ||
76 | if (!bo) | ||
77 | throw invalid_argument(string("omap_bo_new failed: ") + strerror(errno)); | ||
78 | } else { | ||
79 | unsigned bitspertiler; | ||
80 | |||
81 | switch (m_format) { | ||
82 | case PixelFormat::NV12: | ||
83 | bitspertiler = i == 0 ? 8 : 16; break; | ||
84 | case PixelFormat::YUYV: | ||
85 | case PixelFormat::UYVY: | ||
86 | bitspertiler = 32; break; | ||
87 | case PixelFormat::ARGB8888: | ||
88 | case PixelFormat::XRGB8888: | ||
89 | bitspertiler = 32; break; | ||
90 | case PixelFormat::RGB565: | ||
91 | bitspertiler = 16; break; | ||
92 | default: | ||
93 | throw invalid_argument("unimplemented format"); | ||
94 | } | ||
95 | |||
96 | switch (bitspertiler) { | ||
97 | case 8: flags |= OMAP_BO_TILED_8; break; | ||
98 | case 16: flags |= OMAP_BO_TILED_16; break; | ||
99 | case 32: flags |= OMAP_BO_TILED_32; break; | ||
100 | default: | ||
101 | throw invalid_argument("bad bitspertiler"); | ||
102 | } | ||
103 | |||
104 | uint32_t width_tiler = width() * pi.bitspp / bitspertiler; | ||
105 | |||
106 | bo = omap_bo_new_tiled(m_omap_card.dev(), width_tiler, height(), flags); | ||
107 | if (!bo) | ||
108 | throw invalid_argument(string("omap_bo_new_tiled failed: ") + strerror(errno)); | ||
109 | |||
110 | stride = round_up(width() * pi.bitspp / 8, PAGE_SIZE); | ||
111 | } | ||
112 | |||
113 | plane.omap_bo = bo; | ||
114 | plane.handle = omap_bo_handle(bo); | ||
115 | plane.stride = stride; | ||
116 | plane.size = omap_bo_size(bo); | ||
117 | plane.offset = 0; | ||
118 | plane.map = 0; | ||
119 | plane.prime_fd = -1; | ||
120 | } | ||
121 | |||
122 | /* create framebuffer object */ | ||
123 | uint32_t bo_handles[4] = { m_planes[0].handle, m_planes[1].handle }; | ||
124 | uint32_t pitches[4] = { m_planes[0].stride, m_planes[1].stride }; | ||
125 | uint32_t offsets[4] = { m_planes[0].offset, m_planes[1].offset }; | ||
126 | uint32_t id; | ||
127 | int r = drmModeAddFB2(card().fd(), width(), height(), (uint32_t)format(), | ||
128 | bo_handles, pitches, offsets, &id, 0); | ||
129 | if (r) | ||
130 | throw invalid_argument(string("drmModeAddFB2 failed: ") + strerror(errno)); | ||
131 | |||
132 | set_id(id); | ||
133 | } | ||
134 | |||
135 | void OmapFramebuffer::Destroy() | ||
136 | { | ||
137 | /* delete framebuffer */ | ||
138 | drmModeRmFB(card().fd(), id()); | ||
139 | |||
140 | for (uint i = 0; i < m_num_planes; ++i) { | ||
141 | FramebufferPlane& plane = m_planes[i]; | ||
142 | |||
143 | /* unmap buffer */ | ||
144 | if (plane.map) | ||
145 | munmap(plane.map, plane.size); | ||
146 | |||
147 | omap_bo_del(plane.omap_bo); | ||
148 | |||
149 | if (plane.prime_fd >= 0) | ||
150 | ::close(plane.prime_fd); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | uint8_t* OmapFramebuffer::map(unsigned plane) | ||
155 | { | ||
156 | FramebufferPlane& p = m_planes[plane]; | ||
157 | |||
158 | if (p.map) | ||
159 | return p.map; | ||
160 | |||
161 | p.map = (uint8_t*)omap_bo_map(p.omap_bo); | ||
162 | if (p.map == MAP_FAILED) | ||
163 | throw invalid_argument(string("mmap failed: ") + strerror(errno)); | ||
164 | |||
165 | return p.map; | ||
166 | } | ||
167 | |||
168 | int OmapFramebuffer::prime_fd(unsigned int plane) | ||
169 | { | ||
170 | FramebufferPlane& p = m_planes[plane]; | ||
171 | |||
172 | if (p.prime_fd >= 0) | ||
173 | return p.prime_fd; | ||
174 | |||
175 | int fd = omap_bo_dmabuf(p.omap_bo); | ||
176 | if (fd < 0) | ||
177 | throw std::runtime_error("omap_bo_dmabuf failed\n"); | ||
178 | |||
179 | p.prime_fd = fd; | ||
180 | |||
181 | return fd; | ||
182 | } | ||
183 | |||
184 | } | ||
diff --git a/kms++/src/pixelformats.cpp b/kms++/src/pixelformats.cpp new file mode 100644 index 0000000..84ea924 --- /dev/null +++ b/kms++/src/pixelformats.cpp | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <map> | ||
2 | |||
3 | #include <kms++/pixelformats.h> | ||
4 | |||
5 | using namespace std; | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | static const map<PixelFormat, PixelFormatInfo> format_info_array = { | ||
10 | /* YUV packed */ | ||
11 | { PixelFormat::UYVY, { 1, { { 16, 2, 1 } }, } }, | ||
12 | { PixelFormat::YUYV, { 1, { { 16, 2, 1 } }, } }, | ||
13 | { PixelFormat::YVYU, { 1, { { 16, 2, 1 } }, } }, | ||
14 | { PixelFormat::VYUY, { 1, { { 16, 2, 1 } }, } }, | ||
15 | /* YUV semi-planar */ | ||
16 | { PixelFormat::NV12, { 2, { { 8, 1, 1, }, { 8, 2, 2 } }, } }, | ||
17 | { PixelFormat::NV21, { 2, { { 8, 1, 1, }, { 8, 2, 2 } }, } }, | ||
18 | /* RGB16 */ | ||
19 | { PixelFormat::RGB565, { 1, { { 16, 1, 1 } }, } }, | ||
20 | { PixelFormat::BGR565, { 1, { { 16, 1, 1 } }, } }, | ||
21 | /* RGB24 */ | ||
22 | { PixelFormat::RGB888, { 1, { { 24, 1, 1 } }, } }, | ||
23 | { PixelFormat::BGR888, { 1, { { 24, 1, 1 } }, } }, | ||
24 | /* RGB32 */ | ||
25 | { PixelFormat::XRGB8888, { 1, { { 32, 1, 1 } }, } }, | ||
26 | { PixelFormat::XBGR8888, { 1, { { 32, 1, 1 } }, } }, | ||
27 | { PixelFormat::ARGB8888, { 1, { { 32, 1, 1 } }, } }, | ||
28 | { PixelFormat::ABGR8888, { 1, { { 32, 1, 1 } }, } }, | ||
29 | }; | ||
30 | |||
31 | const struct PixelFormatInfo& get_pixel_format_info(PixelFormat format) | ||
32 | { | ||
33 | return format_info_array.at(format); | ||
34 | } | ||
35 | |||
36 | } | ||
diff --git a/kms++/src/plane.cpp b/kms++/src/plane.cpp new file mode 100644 index 0000000..f68c8d0 --- /dev/null +++ b/kms++/src/plane.cpp | |||
@@ -0,0 +1,140 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <iostream> | ||
3 | #include <unistd.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <cassert> | ||
6 | #include <xf86drm.h> | ||
7 | #include <xf86drmMode.h> | ||
8 | #include <algorithm> | ||
9 | |||
10 | #include <kms++/kms++.h> | ||
11 | |||
12 | using namespace std; | ||
13 | |||
14 | namespace kms | ||
15 | { | ||
16 | |||
17 | struct PlanePriv | ||
18 | { | ||
19 | drmModePlanePtr drm_plane; | ||
20 | }; | ||
21 | |||
22 | Plane::Plane(Card &card, uint32_t id, uint32_t idx) | ||
23 | :DrmPropObject(card, id, DRM_MODE_OBJECT_PLANE, idx) | ||
24 | { | ||
25 | m_priv = new PlanePriv(); | ||
26 | m_priv->drm_plane = drmModeGetPlane(this->card().fd(), this->id()); | ||
27 | assert(m_priv->drm_plane); | ||
28 | } | ||
29 | |||
30 | Plane::~Plane() | ||
31 | { | ||
32 | drmModeFreePlane(m_priv->drm_plane); | ||
33 | delete m_priv; | ||
34 | } | ||
35 | |||
36 | bool Plane::supports_crtc(Crtc* crtc) const | ||
37 | { | ||
38 | return m_priv->drm_plane->possible_crtcs & (1 << crtc->idx()); | ||
39 | } | ||
40 | |||
41 | bool Plane::supports_format(PixelFormat fmt) const | ||
42 | { | ||
43 | auto p = m_priv->drm_plane; | ||
44 | |||
45 | for (unsigned i = 0; i < p->count_formats; ++i) | ||
46 | if ((uint32_t)fmt == p->formats[i]) | ||
47 | return true; | ||
48 | |||
49 | return false; | ||
50 | } | ||
51 | |||
52 | PlaneType Plane::plane_type() const | ||
53 | { | ||
54 | if (card().has_has_universal_planes()) { | ||
55 | switch (get_prop_value("type")) { | ||
56 | case DRM_PLANE_TYPE_OVERLAY: | ||
57 | return PlaneType::Overlay; | ||
58 | case DRM_PLANE_TYPE_PRIMARY: | ||
59 | return PlaneType::Primary; | ||
60 | case DRM_PLANE_TYPE_CURSOR: | ||
61 | return PlaneType::Cursor; | ||
62 | default: | ||
63 | throw invalid_argument("Bad plane type"); | ||
64 | } | ||
65 | } else { | ||
66 | return PlaneType::Overlay; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | vector<Crtc*> Plane::get_possible_crtcs() const | ||
71 | { | ||
72 | unsigned idx = 0; | ||
73 | vector<Crtc*> v; | ||
74 | auto crtcs = card().get_crtcs(); | ||
75 | |||
76 | for (uint32_t crtc_mask = m_priv->drm_plane->possible_crtcs; | ||
77 | crtc_mask; | ||
78 | idx++, crtc_mask >>= 1) { | ||
79 | |||
80 | if ((crtc_mask & 1) == 0) | ||
81 | continue; | ||
82 | |||
83 | auto iter = find_if(crtcs.begin(), crtcs.end(), [idx](Crtc* crtc) { return crtc->idx() == idx; }); | ||
84 | |||
85 | if (iter == crtcs.end()) | ||
86 | throw runtime_error("get_possible_crtcs: crtc missing"); | ||
87 | |||
88 | v.push_back(*iter); | ||
89 | } | ||
90 | |||
91 | return v; | ||
92 | } | ||
93 | |||
94 | vector<PixelFormat> Plane::get_formats() const | ||
95 | { | ||
96 | auto p = m_priv->drm_plane; | ||
97 | vector<PixelFormat> r; | ||
98 | |||
99 | for (unsigned i = 0; i < p->count_formats; ++i) | ||
100 | r.push_back((PixelFormat) p->formats[i]); | ||
101 | |||
102 | return r; | ||
103 | } | ||
104 | |||
105 | uint32_t Plane::crtc_id() const | ||
106 | { | ||
107 | return m_priv->drm_plane->crtc_id; | ||
108 | } | ||
109 | |||
110 | uint32_t Plane::fb_id() const | ||
111 | { | ||
112 | return m_priv->drm_plane->fb_id; | ||
113 | } | ||
114 | |||
115 | uint32_t Plane::crtc_x() const | ||
116 | { | ||
117 | return m_priv->drm_plane->crtc_x; | ||
118 | } | ||
119 | |||
120 | uint32_t Plane::crtc_y() const | ||
121 | { | ||
122 | return m_priv->drm_plane->crtc_y; | ||
123 | } | ||
124 | |||
125 | uint32_t Plane::x() const | ||
126 | { | ||
127 | return m_priv->drm_plane->x; | ||
128 | } | ||
129 | |||
130 | uint32_t Plane::y() const | ||
131 | { | ||
132 | return m_priv->drm_plane->y; | ||
133 | } | ||
134 | |||
135 | uint32_t Plane::gamma_size() const | ||
136 | { | ||
137 | return m_priv->drm_plane->gamma_size; | ||
138 | } | ||
139 | |||
140 | } | ||
diff --git a/kms++/src/property.cpp b/kms++/src/property.cpp new file mode 100644 index 0000000..ee79922 --- /dev/null +++ b/kms++/src/property.cpp | |||
@@ -0,0 +1,87 @@ | |||
1 | #include <xf86drm.h> | ||
2 | #include <xf86drmMode.h> | ||
3 | |||
4 | #include <kms++/kms++.h> | ||
5 | |||
6 | using namespace std; | ||
7 | |||
8 | namespace kms | ||
9 | { | ||
10 | |||
11 | struct PropertyPriv | ||
12 | { | ||
13 | drmModePropertyPtr drm_prop; | ||
14 | }; | ||
15 | |||
16 | Property::Property(Card& card, uint32_t id) | ||
17 | : DrmObject(card, id, DRM_MODE_OBJECT_PROPERTY) | ||
18 | { | ||
19 | m_priv = new PropertyPriv(); | ||
20 | m_priv->drm_prop = drmModeGetProperty(card.fd(), id); | ||
21 | m_name = m_priv->drm_prop->name; | ||
22 | |||
23 | PropertyType t; | ||
24 | drmModePropertyPtr p = m_priv->drm_prop; | ||
25 | if (drm_property_type_is(p, DRM_MODE_PROP_BITMASK)) | ||
26 | t = PropertyType::Bitmask; | ||
27 | else if (drm_property_type_is(p, DRM_MODE_PROP_BLOB)) | ||
28 | t = PropertyType::Blob; | ||
29 | else if (drm_property_type_is(p, DRM_MODE_PROP_ENUM)) | ||
30 | t = PropertyType::Enum; | ||
31 | else if (drm_property_type_is(p, DRM_MODE_PROP_OBJECT)) | ||
32 | t = PropertyType::Object; | ||
33 | else if (drm_property_type_is(p, DRM_MODE_PROP_RANGE)) | ||
34 | t = PropertyType::Range; | ||
35 | else if (drm_property_type_is(p, DRM_MODE_PROP_SIGNED_RANGE)) | ||
36 | t = PropertyType::SignedRange; | ||
37 | else | ||
38 | throw invalid_argument("Invalid property type"); | ||
39 | |||
40 | m_type = t; | ||
41 | } | ||
42 | |||
43 | Property::~Property() | ||
44 | { | ||
45 | drmModeFreeProperty(m_priv->drm_prop); | ||
46 | delete m_priv; | ||
47 | } | ||
48 | |||
49 | const string& Property::name() const | ||
50 | { | ||
51 | return m_name; | ||
52 | } | ||
53 | |||
54 | bool Property::is_immutable() const | ||
55 | { | ||
56 | return m_priv->drm_prop->flags & DRM_MODE_PROP_IMMUTABLE; | ||
57 | } | ||
58 | |||
59 | bool Property::is_pending() const | ||
60 | { | ||
61 | return m_priv->drm_prop->flags & DRM_MODE_PROP_PENDING; | ||
62 | } | ||
63 | |||
64 | vector<uint64_t> Property::get_values() const | ||
65 | { | ||
66 | drmModePropertyPtr p = m_priv->drm_prop; | ||
67 | return vector<uint64_t>(p->values, p->values + p->count_values); | ||
68 | } | ||
69 | |||
70 | map<uint64_t, string> Property::get_enums() const | ||
71 | { | ||
72 | drmModePropertyPtr p = m_priv->drm_prop; | ||
73 | |||
74 | map<uint64_t, string> map; | ||
75 | |||
76 | for (int i = 0; i < p->count_enums; ++i) | ||
77 | map[p->enums[i].value] = string(p->enums[i].name); | ||
78 | |||
79 | return map; | ||
80 | } | ||
81 | |||
82 | vector<uint32_t> Property::get_blob_ids() const | ||
83 | { | ||
84 | drmModePropertyPtr p = m_priv->drm_prop; | ||
85 | return vector<uint32_t>(p->blob_ids, p->blob_ids + p->count_blobs); | ||
86 | } | ||
87 | } | ||
diff --git a/kms++/src/videomode.cpp b/kms++/src/videomode.cpp new file mode 100644 index 0000000..b8bd797 --- /dev/null +++ b/kms++/src/videomode.cpp | |||
@@ -0,0 +1,121 @@ | |||
1 | #include <xf86drm.h> | ||
2 | #include <xf86drmMode.h> | ||
3 | #include <math.h> | ||
4 | #include <sstream> | ||
5 | |||
6 | #include <kms++/kms++.h> | ||
7 | #include "helpers.h" | ||
8 | |||
9 | using namespace std; | ||
10 | |||
11 | namespace kms | ||
12 | { | ||
13 | |||
14 | unique_ptr<Blob> Videomode::to_blob(Card& card) const | ||
15 | { | ||
16 | drmModeModeInfo drm_mode = video_mode_to_drm_mode(*this); | ||
17 | |||
18 | return unique_ptr<Blob>(new Blob(card, &drm_mode, sizeof(drm_mode))); | ||
19 | } | ||
20 | |||
21 | float Videomode::calculated_vrefresh() const | ||
22 | { | ||
23 | // XXX interlace should only halve visible vertical lines, not blanking | ||
24 | float refresh = (clock * 1000.0) / (htotal * vtotal) * (interlace() ? 2 : 1); | ||
25 | return roundf(refresh * 100.0) / 100.0; | ||
26 | } | ||
27 | |||
28 | bool Videomode::interlace() const | ||
29 | { | ||
30 | return flags & DRM_MODE_FLAG_INTERLACE; | ||
31 | } | ||
32 | |||
33 | SyncPolarity Videomode::hsync() const | ||
34 | { | ||
35 | if (flags & DRM_MODE_FLAG_PHSYNC) | ||
36 | return SyncPolarity::Positive; | ||
37 | if (flags & DRM_MODE_FLAG_NHSYNC) | ||
38 | return SyncPolarity::Negative; | ||
39 | return SyncPolarity::Undefined; | ||
40 | } | ||
41 | |||
42 | SyncPolarity Videomode::vsync() const | ||
43 | { | ||
44 | if (flags & DRM_MODE_FLAG_PVSYNC) | ||
45 | return SyncPolarity::Positive; | ||
46 | if (flags & DRM_MODE_FLAG_NVSYNC) | ||
47 | return SyncPolarity::Negative; | ||
48 | return SyncPolarity::Undefined; | ||
49 | } | ||
50 | |||
51 | void Videomode::set_interlace(bool ilace) | ||
52 | { | ||
53 | if (ilace) | ||
54 | flags |= DRM_MODE_FLAG_INTERLACE; | ||
55 | else | ||
56 | flags &= ~DRM_MODE_FLAG_INTERLACE; | ||
57 | } | ||
58 | |||
59 | void Videomode::set_hsync(SyncPolarity pol) | ||
60 | { | ||
61 | flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC); | ||
62 | |||
63 | switch (pol) { | ||
64 | case SyncPolarity::Positive: | ||
65 | flags |= DRM_MODE_FLAG_PHSYNC; | ||
66 | break; | ||
67 | case SyncPolarity::Negative: | ||
68 | flags |= DRM_MODE_FLAG_NHSYNC; | ||
69 | break; | ||
70 | default: | ||
71 | break; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void Videomode::set_vsync(SyncPolarity pol) | ||
76 | { | ||
77 | flags &= ~(DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC); | ||
78 | |||
79 | switch (pol) { | ||
80 | case SyncPolarity::Positive: | ||
81 | flags |= DRM_MODE_FLAG_PVSYNC; | ||
82 | break; | ||
83 | case SyncPolarity::Negative: | ||
84 | flags |= DRM_MODE_FLAG_NVSYNC; | ||
85 | break; | ||
86 | default: | ||
87 | break; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | string Videomode::to_string() const | ||
92 | { | ||
93 | std::stringstream ss; | ||
94 | ss << hdisplay << "x" << vdisplay; | ||
95 | if (interlace()) | ||
96 | ss << "i"; | ||
97 | ss << "@" << calculated_vrefresh(); | ||
98 | return ss.str(); | ||
99 | } | ||
100 | |||
101 | Videomode videomode_from_timings(uint32_t clock_khz, | ||
102 | uint16_t hact, uint16_t hfp, uint16_t hsw, uint16_t hbp, | ||
103 | uint16_t vact, uint16_t vfp, uint16_t vsw, uint16_t vbp) | ||
104 | { | ||
105 | Videomode m { }; | ||
106 | m.clock = clock_khz; | ||
107 | |||
108 | m.hdisplay = hact; | ||
109 | m.hsync_start = hact + hfp; | ||
110 | m.hsync_end = hact + hfp + hsw; | ||
111 | m.htotal = hact + hfp + hsw + hbp; | ||
112 | |||
113 | m.vdisplay = vact; | ||
114 | m.vsync_start = vact + vfp; | ||
115 | m.vsync_end = vact + vfp + vsw; | ||
116 | m.vtotal = vact + vfp + vsw + vbp; | ||
117 | |||
118 | return m; | ||
119 | } | ||
120 | |||
121 | } | ||
diff --git a/kms++util/CMakeLists.txt b/kms++util/CMakeLists.txt new file mode 100644 index 0000000..2fc15e3 --- /dev/null +++ b/kms++util/CMakeLists.txt | |||
@@ -0,0 +1,18 @@ | |||
1 | file(GLOB SRCS "src/*.cpp" "src/*.h") | ||
2 | file(GLOB PUB_HDRS "inc/kms++util/*.h") | ||
3 | add_library(kms++util ${SRCS} ${PUB_HDRS}) | ||
4 | |||
5 | target_include_directories(kms++util PUBLIC | ||
6 | $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> | ||
7 | $<INSTALL_INTERFACE:include> | ||
8 | PRIVATE src) | ||
9 | |||
10 | target_link_libraries(kms++util kms++ pthread) | ||
11 | |||
12 | set_target_properties(kms++util PROPERTIES | ||
13 | PUBLIC_HEADER "${PUB_HDRS}") | ||
14 | |||
15 | install(TARGETS kms++util | ||
16 | LIBRARY DESTINATION lib | ||
17 | ARCHIVE DESTINATION lib | ||
18 | PUBLIC_HEADER DESTINATION include/kms++util) | ||
diff --git a/kms++util/inc/kms++util/color.h b/kms++util/inc/kms++util/color.h new file mode 100644 index 0000000..f378433 --- /dev/null +++ b/kms++util/inc/kms++util/color.h | |||
@@ -0,0 +1,49 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <cstdint> | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | struct YUV; | ||
8 | |||
9 | enum class YUVType { | ||
10 | BT601_Lim = 0, | ||
11 | BT601_Full, | ||
12 | BT709_Lim, | ||
13 | BT709_Full, | ||
14 | MAX, | ||
15 | }; | ||
16 | |||
17 | struct RGB | ||
18 | { | ||
19 | RGB(); | ||
20 | RGB(uint8_t r, uint8_t g, uint8_t b); | ||
21 | RGB(uint8_t a, uint8_t r, uint8_t g, uint8_t b); | ||
22 | RGB(uint32_t argb); | ||
23 | |||
24 | uint32_t rgb888() const; | ||
25 | uint32_t bgr888() const; | ||
26 | uint32_t argb8888() const; | ||
27 | uint32_t abgr8888() const; | ||
28 | uint16_t rgb565() const; | ||
29 | uint16_t bgr565() const; | ||
30 | YUV yuv(YUVType type = YUVType::BT601_Lim) const; | ||
31 | |||
32 | uint8_t b; | ||
33 | uint8_t g; | ||
34 | uint8_t r; | ||
35 | uint8_t a; | ||
36 | }; | ||
37 | |||
38 | struct YUV | ||
39 | { | ||
40 | YUV(); | ||
41 | YUV(uint8_t y, uint8_t u, uint8_t v); | ||
42 | YUV(const RGB& rgb, YUVType type = YUVType::BT601_Lim); | ||
43 | |||
44 | uint8_t v; | ||
45 | uint8_t u; | ||
46 | uint8_t y; | ||
47 | uint8_t a; | ||
48 | }; | ||
49 | } | ||
diff --git a/kms++util/inc/kms++util/cpuframebuffer.h b/kms++util/inc/kms++util/cpuframebuffer.h new file mode 100644 index 0000000..4273e0d --- /dev/null +++ b/kms++util/inc/kms++util/cpuframebuffer.h | |||
@@ -0,0 +1,44 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/kms++.h> | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | |||
8 | class CPUFramebuffer : public IFramebuffer { | ||
9 | public: | ||
10 | CPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format); | ||
11 | |||
12 | virtual ~CPUFramebuffer(); | ||
13 | |||
14 | CPUFramebuffer(const CPUFramebuffer& other) = delete; | ||
15 | CPUFramebuffer& operator=(const CPUFramebuffer& other) = delete; | ||
16 | |||
17 | uint32_t width() const { return m_width; } | ||
18 | uint32_t height() const { return m_height; } | ||
19 | |||
20 | PixelFormat format() const { return m_format; } | ||
21 | unsigned num_planes() const { return m_num_planes; } | ||
22 | |||
23 | uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } | ||
24 | uint32_t size(unsigned plane) const { return m_planes[plane].size; } | ||
25 | uint32_t offset(unsigned plane) const { return m_planes[plane].offset; } | ||
26 | uint8_t* map(unsigned plane) { return m_planes[plane].map; } | ||
27 | |||
28 | private: | ||
29 | struct FramebufferPlane { | ||
30 | uint32_t size; | ||
31 | uint32_t stride; | ||
32 | uint32_t offset; | ||
33 | uint8_t *map; | ||
34 | }; | ||
35 | |||
36 | uint32_t m_width; | ||
37 | uint32_t m_height; | ||
38 | PixelFormat m_format; | ||
39 | |||
40 | unsigned m_num_planes; | ||
41 | struct FramebufferPlane m_planes[4]; | ||
42 | }; | ||
43 | |||
44 | } | ||
diff --git a/kms++util/inc/kms++util/extcpuframebuffer.h b/kms++util/inc/kms++util/extcpuframebuffer.h new file mode 100644 index 0000000..92ca43a --- /dev/null +++ b/kms++util/inc/kms++util/extcpuframebuffer.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/kms++.h> | ||
4 | |||
5 | namespace kms | ||
6 | { | ||
7 | |||
8 | class ExtCPUFramebuffer : public IFramebuffer | ||
9 | { | ||
10 | public: | ||
11 | ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, | ||
12 | uint8_t* buffer, uint32_t size, uint32_t pitch, uint32_t offset); | ||
13 | ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, | ||
14 | uint8_t* buffers[4], uint32_t sizes[4], uint32_t pitches[4], uint32_t offsets[4]); | ||
15 | virtual ~ExtCPUFramebuffer(); | ||
16 | |||
17 | uint32_t width() const { return m_width; } | ||
18 | uint32_t height() const { return m_height; } | ||
19 | |||
20 | PixelFormat format() const { return m_format; } | ||
21 | unsigned num_planes() const { return m_num_planes; } | ||
22 | |||
23 | uint32_t stride(unsigned plane) const { return m_planes[plane].stride; } | ||
24 | uint32_t size(unsigned plane) const { return m_planes[plane].size; } | ||
25 | uint32_t offset(unsigned plane) const { return m_planes[plane].offset; } | ||
26 | uint8_t* map(unsigned plane) { return m_planes[plane].map; } | ||
27 | |||
28 | private: | ||
29 | struct FramebufferPlane { | ||
30 | uint32_t size; | ||
31 | uint32_t stride; | ||
32 | uint32_t offset; | ||
33 | uint8_t *map; | ||
34 | }; | ||
35 | |||
36 | uint32_t m_width; | ||
37 | uint32_t m_height; | ||
38 | PixelFormat m_format; | ||
39 | |||
40 | unsigned m_num_planes; | ||
41 | struct FramebufferPlane m_planes[4]; | ||
42 | }; | ||
43 | } | ||
diff --git a/kms++util/inc/kms++util/kms++util.h b/kms++util/inc/kms++util/kms++util.h new file mode 100644 index 0000000..8e45b0d --- /dev/null +++ b/kms++util/inc/kms++util/kms++util.h | |||
@@ -0,0 +1,71 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <kms++/kms++.h> | ||
4 | |||
5 | #include <kms++util/color.h> | ||
6 | #include <kms++util/strhelpers.h> | ||
7 | #include <kms++util/cpuframebuffer.h> | ||
8 | #include <kms++util/extcpuframebuffer.h> | ||
9 | #include <kms++util/stopwatch.h> | ||
10 | #include <kms++util/opts.h> | ||
11 | #include <kms++util/resourcemanager.h> | ||
12 | |||
13 | #include <cstdio> | ||
14 | #include <cstdlib> | ||
15 | |||
16 | namespace kms | ||
17 | { | ||
18 | class IFramebuffer; | ||
19 | |||
20 | void draw_rgb_pixel(IFramebuffer& buf, unsigned x, unsigned y, RGB color); | ||
21 | void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2); | ||
22 | void draw_yuv420_macropixel(IFramebuffer& buf, unsigned x, unsigned y, | ||
23 | YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4); | ||
24 | void draw_rect(IFramebuffer &fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color); | ||
25 | void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const std::string& str, RGB color); | ||
26 | |||
27 | void draw_color_bar(IFramebuffer& buf, int old_xpos, int xpos, int width); | ||
28 | |||
29 | void draw_test_pattern(IFramebuffer &fb, YUVType yuvt = YUVType::BT601_Lim); | ||
30 | } | ||
31 | |||
32 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | ||
33 | |||
34 | #define unlikely(x) __builtin_expect(!!(x), 0) | ||
35 | |||
36 | /* __STRING(x) is a glibcism (i.e. not standard), which happens to also | ||
37 | * be available in uClibc. However, musl does not define it. Do it here. | ||
38 | */ | ||
39 | #ifndef __STRING | ||
40 | #define __STRING(x) #x | ||
41 | #endif | ||
42 | |||
43 | #define ASSERT(x) \ | ||
44 | if (unlikely(!(x))) { \ | ||
45 | fprintf(stderr, "%s:%d: %s: ASSERT(%s) failed\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, __STRING(x)); \ | ||
46 | abort(); \ | ||
47 | } | ||
48 | |||
49 | #define FAIL(fmt, ...) \ | ||
50 | do { \ | ||
51 | fprintf(stderr, "%s:%d: %s:\n" fmt "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__); \ | ||
52 | abort(); \ | ||
53 | } while(0) | ||
54 | |||
55 | #define FAIL_IF(x, fmt, ...) \ | ||
56 | if (unlikely(x)) { \ | ||
57 | fprintf(stderr, "%s:%d: %s:\n" fmt "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__); \ | ||
58 | abort(); \ | ||
59 | } | ||
60 | |||
61 | #define EXIT(fmt, ...) \ | ||
62 | do { \ | ||
63 | fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ | ||
64 | exit(-1); \ | ||
65 | } while(0) | ||
66 | |||
67 | #define EXIT_IF(x, fmt, ...) \ | ||
68 | if (unlikely(x)) { \ | ||
69 | fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ | ||
70 | exit(-1); \ | ||
71 | } | ||
diff --git a/kms++util/inc/kms++util/opts.h b/kms++util/inc/kms++util/opts.h new file mode 100644 index 0000000..1b0fd22 --- /dev/null +++ b/kms++util/inc/kms++util/opts.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <string> | ||
4 | #include <vector> | ||
5 | #include <functional> | ||
6 | |||
7 | class Option | ||
8 | { | ||
9 | friend class OptionSet; | ||
10 | public: | ||
11 | Option(const std::string& str, std::function<void()> func); | ||
12 | Option(const std::string& str, std::function<void(const std::string)> func); | ||
13 | |||
14 | private: | ||
15 | void parse(const std::string& str); | ||
16 | |||
17 | char m_short; | ||
18 | std::string m_long; | ||
19 | int m_has_arg; | ||
20 | std::function<void()> m_void_func; | ||
21 | std::function<void(const std::string)> m_func; | ||
22 | }; | ||
23 | |||
24 | class OptionSet | ||
25 | { | ||
26 | public: | ||
27 | OptionSet(std::initializer_list<Option> il); | ||
28 | |||
29 | void parse(int argc, char** argv); | ||
30 | |||
31 | const std::vector<std::string> params() const { return m_params; } | ||
32 | |||
33 | private: | ||
34 | const Option& find_opt(int c); | ||
35 | |||
36 | const std::vector<Option> m_opts; | ||
37 | std::vector<std::string> m_params; | ||
38 | }; | ||
diff --git a/kms++util/inc/kms++util/resourcemanager.h b/kms++util/inc/kms++util/resourcemanager.h new file mode 100644 index 0000000..1b5cf21 --- /dev/null +++ b/kms++util/inc/kms++util/resourcemanager.h | |||
@@ -0,0 +1,32 @@ | |||
1 | #include <kms++/kms++.h> | ||
2 | #include <set> | ||
3 | #include <string> | ||
4 | |||
5 | namespace kms { | ||
6 | |||
7 | class ResourceManager | ||
8 | { | ||
9 | public: | ||
10 | ResourceManager(Card& card); | ||
11 | |||
12 | void reset(); | ||
13 | |||
14 | Card& card() const { return m_card; } | ||
15 | Connector* reserve_connector(const std::string& name = ""); | ||
16 | Connector* reserve_connector(Connector* conn); | ||
17 | Crtc* reserve_crtc(Connector* conn); | ||
18 | Crtc* reserve_crtc(Crtc* crtc); | ||
19 | Plane* reserve_plane(Crtc* crtc, PlaneType type, PixelFormat format = PixelFormat::Undefined); | ||
20 | Plane* reserve_plane(Plane* plane); | ||
21 | Plane* reserve_generic_plane(Crtc* crtc, PixelFormat format = PixelFormat::Undefined); | ||
22 | Plane* reserve_primary_plane(Crtc* crtc, PixelFormat format = PixelFormat::Undefined); | ||
23 | Plane* reserve_overlay_plane(Crtc* crtc, PixelFormat format = PixelFormat::Undefined); | ||
24 | |||
25 | private: | ||
26 | Card& m_card; | ||
27 | std::set<Connector*> m_reserved_connectors; | ||
28 | std::set<Crtc*> m_reserved_crtcs; | ||
29 | std::set<Plane*> m_reserved_planes; | ||
30 | }; | ||
31 | |||
32 | } | ||
diff --git a/kms++util/inc/kms++util/stopwatch.h b/kms++util/inc/kms++util/stopwatch.h new file mode 100644 index 0000000..9b60fa1 --- /dev/null +++ b/kms++util/inc/kms++util/stopwatch.h | |||
@@ -0,0 +1,28 @@ | |||
1 | #include <chrono> | ||
2 | |||
3 | class Stopwatch | ||
4 | { | ||
5 | public: | ||
6 | void start() | ||
7 | { | ||
8 | m_start = std::chrono::steady_clock::now(); | ||
9 | } | ||
10 | |||
11 | double elapsed_s() const | ||
12 | { | ||
13 | return std::chrono::duration<double>(std::chrono::steady_clock::now() - m_start).count(); | ||
14 | } | ||
15 | |||
16 | double elapsed_ms() const | ||
17 | { | ||
18 | return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - m_start).count(); | ||
19 | } | ||
20 | |||
21 | double elapsed_us() const | ||
22 | { | ||
23 | return std::chrono::duration<double, std::micro>(std::chrono::steady_clock::now() - m_start).count(); | ||
24 | } | ||
25 | |||
26 | private: | ||
27 | std::chrono::steady_clock::time_point m_start; | ||
28 | }; | ||
diff --git a/kms++util/inc/kms++util/strhelpers.h b/kms++util/inc/kms++util/strhelpers.h new file mode 100644 index 0000000..2c540f3 --- /dev/null +++ b/kms++util/inc/kms++util/strhelpers.h | |||
@@ -0,0 +1,33 @@ | |||
1 | #include <sstream> | ||
2 | #include <string> | ||
3 | #include <vector> | ||
4 | #include <functional> | ||
5 | |||
6 | std::string to_lower(const std::string& str); | ||
7 | |||
8 | template <typename T> | ||
9 | std::string join(const T& values, const std::string& delim) | ||
10 | { | ||
11 | std::ostringstream ss; | ||
12 | for (const auto& v : values) { | ||
13 | if (&v != &values[0]) | ||
14 | ss << delim; | ||
15 | ss << v; | ||
16 | } | ||
17 | return ss.str(); | ||
18 | } | ||
19 | |||
20 | template <typename T> | ||
21 | std::string join(const std::vector<T>& values, const std::string& delim, std::function<std::string(T)> func) | ||
22 | { | ||
23 | std::ostringstream ss; | ||
24 | for (const auto& v : values) { | ||
25 | if (&v != &values[0]) | ||
26 | ss << delim; | ||
27 | ss << func(v); | ||
28 | } | ||
29 | return ss.str(); | ||
30 | } | ||
31 | |||
32 | std::string sformat(const char *fmt, ...) | ||
33 | __attribute__ ((format (printf, 1, 2))); | ||
diff --git a/kms++util/inc/kms++util/videodevice.h b/kms++util/inc/kms++util/videodevice.h new file mode 100644 index 0000000..68e2b01 --- /dev/null +++ b/kms++util/inc/kms++util/videodevice.h | |||
@@ -0,0 +1,86 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <string> | ||
4 | #include <kms++/kms++.h> | ||
5 | |||
6 | class VideoStreamer; | ||
7 | |||
8 | class VideoDevice | ||
9 | { | ||
10 | public: | ||
11 | struct VideoFrameSize | ||
12 | { | ||
13 | uint32_t min_w, max_w, step_w; | ||
14 | uint32_t min_h, max_h, step_h; | ||
15 | }; | ||
16 | |||
17 | VideoDevice(const std::string& dev); | ||
18 | VideoDevice(int fd); | ||
19 | ~VideoDevice(); | ||
20 | |||
21 | VideoDevice(const VideoDevice& other) = delete; | ||
22 | VideoDevice& operator=(const VideoDevice& other) = delete; | ||
23 | |||
24 | VideoStreamer* get_capture_streamer(); | ||
25 | VideoStreamer* get_output_streamer(); | ||
26 | |||
27 | std::vector<std::tuple<uint32_t, uint32_t>> get_discrete_frame_sizes(kms::PixelFormat fmt); | ||
28 | VideoFrameSize get_frame_sizes(kms::PixelFormat fmt); | ||
29 | |||
30 | int fd() const { return m_fd; } | ||
31 | bool has_capture() const { return m_has_capture; } | ||
32 | bool has_output() const { return m_has_output; } | ||
33 | bool has_m2m() const { return m_has_m2m; } | ||
34 | |||
35 | static std::vector<std::string> get_capture_devices(); | ||
36 | static std::vector<std::string> get_m2m_devices(); | ||
37 | |||
38 | private: | ||
39 | int m_fd; | ||
40 | |||
41 | bool m_has_capture; | ||
42 | bool m_has_mplane_capture; | ||
43 | |||
44 | bool m_has_output; | ||
45 | bool m_has_mplane_output; | ||
46 | |||
47 | bool m_has_m2m; | ||
48 | bool m_has_mplane_m2m; | ||
49 | |||
50 | std::vector<kms::DumbFramebuffer*> m_capture_fbs; | ||
51 | std::vector<kms::DumbFramebuffer*> m_output_fbs; | ||
52 | |||
53 | VideoStreamer* m_capture_streamer; | ||
54 | VideoStreamer* m_output_streamer; | ||
55 | }; | ||
56 | |||
57 | class VideoStreamer | ||
58 | { | ||
59 | public: | ||
60 | enum class StreamerType { | ||
61 | CaptureSingle, | ||
62 | CaptureMulti, | ||
63 | OutputSingle, | ||
64 | OutputMulti, | ||
65 | }; | ||
66 | |||
67 | VideoStreamer(int fd, StreamerType type); | ||
68 | |||
69 | std::vector<std::string> get_ports(); | ||
70 | void set_port(uint32_t index); | ||
71 | |||
72 | std::vector<kms::PixelFormat> get_formats(); | ||
73 | void set_format(kms::PixelFormat fmt, uint32_t width, uint32_t height); | ||
74 | void set_queue_size(uint32_t queue_size); | ||
75 | void queue(kms::DumbFramebuffer* fb); | ||
76 | kms::DumbFramebuffer* dequeue(); | ||
77 | void stream_on(); | ||
78 | void stream_off(); | ||
79 | |||
80 | int fd() const { return m_fd; } | ||
81 | |||
82 | private: | ||
83 | int m_fd; | ||
84 | StreamerType m_type; | ||
85 | std::vector<kms::DumbFramebuffer*> m_fbs; | ||
86 | }; | ||
diff --git a/kms++util/src/color.cpp b/kms++util/src/color.cpp new file mode 100644 index 0000000..2e6f217 --- /dev/null +++ b/kms++util/src/color.cpp | |||
@@ -0,0 +1,147 @@ | |||
1 | #include <kms++util/color.h> | ||
2 | |||
3 | namespace kms | ||
4 | { | ||
5 | RGB::RGB() | ||
6 | { | ||
7 | r = g = b = 0; | ||
8 | a = 255; | ||
9 | } | ||
10 | |||
11 | RGB::RGB(uint8_t r, uint8_t g, uint8_t b) | ||
12 | :RGB(255, r, g, b) | ||
13 | { | ||
14 | } | ||
15 | |||
16 | RGB::RGB(uint8_t a, uint8_t r, uint8_t g, uint8_t b) | ||
17 | { | ||
18 | this->r = r; | ||
19 | this->g = g; | ||
20 | this->b = b; | ||
21 | this->a = a; | ||
22 | } | ||
23 | |||
24 | RGB::RGB(uint32_t argb) | ||
25 | { | ||
26 | this->b = (argb >> 0) & 0xff; | ||
27 | this->g = (argb >> 8) & 0xff; | ||
28 | this->r = (argb >> 16) & 0xff; | ||
29 | this->a = (argb >> 24) & 0xff; | ||
30 | } | ||
31 | |||
32 | uint32_t RGB::rgb888() const | ||
33 | { | ||
34 | return (r << 16) | (g << 8) | (b << 0); | ||
35 | } | ||
36 | |||
37 | uint32_t RGB::bgr888() const | ||
38 | { | ||
39 | return (b << 16) | (g << 8) | (r << 0); | ||
40 | } | ||
41 | |||
42 | uint32_t RGB::argb8888() const | ||
43 | { | ||
44 | return (a << 24) | (r << 16) | (g << 8) | (b << 0); | ||
45 | } | ||
46 | |||
47 | uint32_t RGB::abgr8888() const | ||
48 | { | ||
49 | return (a << 24) | (b << 16) | (g << 8) | (r << 0); | ||
50 | } | ||
51 | |||
52 | uint16_t RGB::rgb565() const | ||
53 | { | ||
54 | return ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0); | ||
55 | } | ||
56 | |||
57 | uint16_t RGB::bgr565() const | ||
58 | { | ||
59 | return ((b >> 3) << 11) | ((g >> 2) << 5) | ((r >> 3) << 0); | ||
60 | } | ||
61 | |||
62 | YUV RGB::yuv(YUVType type) const | ||
63 | { | ||
64 | return YUV(*this, type); | ||
65 | } | ||
66 | |||
67 | #define CF_ONE (256) | ||
68 | #define CF(a, b, c) { ((int) ((a) * CF_ONE)), ((int) ((b) * CF_ONE)), ((int) ((c) * CF_ONE)) } | ||
69 | #define CLAMP(a) ((a) > (CF_ONE-1) ? (CF_ONE-1) : (a) < 0 ? 0 : (a)) | ||
70 | |||
71 | const int YUVcoef[static_cast<unsigned>(YUVType::MAX)][3][3] = { | ||
72 | [static_cast<unsigned>(YUVType::BT601_Lim)] = { | ||
73 | CF( 0.257, 0.504, 0.098), | ||
74 | CF(-0.148, -0.291, 0.439), | ||
75 | CF( 0.439, -0.368, -0.071) }, | ||
76 | [static_cast<unsigned>(YUVType::BT601_Full)] = { | ||
77 | CF( 0.299, 0.587, 0.114), | ||
78 | CF(-0.169, -0.331, 0.500), | ||
79 | CF( 0.500, -0.419, -0.081) }, | ||
80 | [static_cast<unsigned>(YUVType::BT709_Lim)] = { | ||
81 | CF( 0.1826, 0.6142, 0.0620), | ||
82 | CF(-0.1006, -0.3386, 0.4392), | ||
83 | CF( 0.4392, -0.3989, -0.0403) }, | ||
84 | [static_cast<unsigned>(YUVType::BT709_Full)] = { | ||
85 | CF( 0.2126, 0.7152, 0.0722), | ||
86 | CF(-0.1146, -0.3854, 0.5000), | ||
87 | CF( 0.5000, -0.4542, -0.0468) }, | ||
88 | }; | ||
89 | |||
90 | const int YUVoffset[static_cast<unsigned>(YUVType::MAX)][3] = { | ||
91 | [static_cast<unsigned>(YUVType::BT601_Lim)] = CF(0.0625, 0.5, 0.5), | ||
92 | [static_cast<unsigned>(YUVType::BT601_Full)] = CF( 0, 0.5, 0.5), | ||
93 | [static_cast<unsigned>(YUVType::BT709_Lim)] = CF(0.0625, 0.5, 0.5), | ||
94 | [static_cast<unsigned>(YUVType::BT709_Full)] = CF( 0, 0.5, 0.5), | ||
95 | }; | ||
96 | |||
97 | YUV::YUV() | ||
98 | { | ||
99 | y = u = v = a = 0; | ||
100 | } | ||
101 | |||
102 | YUV::YUV(uint8_t y, uint8_t u, uint8_t v) | ||
103 | { | ||
104 | this->y = y; | ||
105 | this->u = u; | ||
106 | this->v = v; | ||
107 | this->a = 0; | ||
108 | } | ||
109 | |||
110 | static inline | ||
111 | uint8_t MAKE_YUV_Y(uint8_t r, uint8_t g, uint8_t b, YUVType type) | ||
112 | { | ||
113 | unsigned tidx = static_cast<unsigned>(type); | ||
114 | |||
115 | return CLAMP(((YUVcoef[tidx][0][0] * r + YUVcoef[tidx][0][1] * g + | ||
116 | YUVcoef[tidx][0][2] * b + CF_ONE/2) / CF_ONE) + | ||
117 | YUVoffset[tidx][0]); | ||
118 | } | ||
119 | |||
120 | static inline | ||
121 | uint8_t MAKE_YUV_U(uint8_t r, uint8_t g, uint8_t b, YUVType type) | ||
122 | { | ||
123 | unsigned tidx = static_cast<unsigned>(type); | ||
124 | |||
125 | return CLAMP(((YUVcoef[tidx][1][0] * r + YUVcoef[tidx][1][1] * g + | ||
126 | YUVcoef[tidx][1][2] * b + CF_ONE/2) / CF_ONE) + | ||
127 | YUVoffset[tidx][1]); | ||
128 | } | ||
129 | |||
130 | static inline | ||
131 | uint8_t MAKE_YUV_V(uint8_t r, uint8_t g, uint8_t b, YUVType type) | ||
132 | { | ||
133 | unsigned tidx = static_cast<unsigned>(type); | ||
134 | |||
135 | return CLAMP(((YUVcoef[tidx][2][0] * r + YUVcoef[tidx][2][1] * g + | ||
136 | YUVcoef[tidx][2][2] * b + CF_ONE/2) / CF_ONE) + | ||
137 | YUVoffset[tidx][2]); | ||
138 | } | ||
139 | |||
140 | YUV::YUV(const RGB& rgb, YUVType type) | ||
141 | { | ||
142 | this->y = MAKE_YUV_Y(rgb.r, rgb.g, rgb.b, type); | ||
143 | this->u = MAKE_YUV_U(rgb.r, rgb.g, rgb.b, type); | ||
144 | this->v = MAKE_YUV_V(rgb.r, rgb.g, rgb.b, type); | ||
145 | this->a = rgb.a; | ||
146 | } | ||
147 | } | ||
diff --git a/kms++util/src/colorbar.cpp b/kms++util/src/colorbar.cpp new file mode 100644 index 0000000..c08ed9d --- /dev/null +++ b/kms++util/src/colorbar.cpp | |||
@@ -0,0 +1,132 @@ | |||
1 | #include <cstdint> | ||
2 | |||
3 | #include <kms++/kms++.h> | ||
4 | #include <kms++util/kms++util.h> | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | static const RGB colors32[] = { | ||
9 | RGB(255, 255, 255), | ||
10 | RGB(255, 0, 0), | ||
11 | RGB(255, 255, 255), | ||
12 | RGB(0, 255, 0), | ||
13 | RGB(255, 255, 255), | ||
14 | RGB(0, 0, 255), | ||
15 | RGB(255, 255, 255), | ||
16 | RGB(200, 200, 200), | ||
17 | RGB(255, 255, 255), | ||
18 | RGB(100, 100, 100), | ||
19 | RGB(255, 255, 255), | ||
20 | RGB(50, 50, 50), | ||
21 | }; | ||
22 | |||
23 | static const uint16_t colors16[] = { | ||
24 | colors32[0].rgb565(), | ||
25 | colors32[1].rgb565(), | ||
26 | colors32[2].rgb565(), | ||
27 | colors32[3].rgb565(), | ||
28 | colors32[4].rgb565(), | ||
29 | colors32[5].rgb565(), | ||
30 | colors32[6].rgb565(), | ||
31 | colors32[7].rgb565(), | ||
32 | colors32[8].rgb565(), | ||
33 | colors32[9].rgb565(), | ||
34 | colors32[10].rgb565(), | ||
35 | colors32[11].rgb565(), | ||
36 | }; | ||
37 | |||
38 | static void drm_draw_color_bar_rgb888(IFramebuffer& buf, int old_xpos, int xpos, int width) | ||
39 | { | ||
40 | for (unsigned y = 0; y < buf.height(); ++y) { | ||
41 | RGB bcol = colors32[y * ARRAY_SIZE(colors32) / buf.height()]; | ||
42 | uint32_t *line = (uint32_t*)(buf.map(0) + buf.stride(0) * y); | ||
43 | |||
44 | if (old_xpos >= 0) { | ||
45 | for (int x = old_xpos; x < old_xpos + width; ++x) | ||
46 | line[x] = 0; | ||
47 | } | ||
48 | |||
49 | for (int x = xpos; x < xpos + width; ++x) | ||
50 | line[x] = bcol.argb8888(); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | static void drm_draw_color_bar_rgb565(IFramebuffer& buf, int old_xpos, int xpos, int width) | ||
55 | { | ||
56 | static_assert(ARRAY_SIZE(colors32) == ARRAY_SIZE(colors16), "bad colors arrays"); | ||
57 | |||
58 | for (unsigned y = 0; y < buf.height(); ++y) { | ||
59 | uint16_t bcol = colors16[y * ARRAY_SIZE(colors16) / buf.height()]; | ||
60 | uint16_t *line = (uint16_t*)(buf.map(0) + buf.stride(0) * y); | ||
61 | |||
62 | if (old_xpos >= 0) { | ||
63 | for (int x = old_xpos; x < old_xpos + width; ++x) | ||
64 | line[x] = 0; | ||
65 | } | ||
66 | |||
67 | for (int x = xpos; x < xpos + width; ++x) | ||
68 | line[x] = bcol; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | static void drm_draw_color_bar_semiplanar_yuv(IFramebuffer& buf, int old_xpos, int xpos, int width) | ||
73 | { | ||
74 | const uint8_t colors[] = { | ||
75 | 0xff, | ||
76 | 0x00, | ||
77 | 0xff, | ||
78 | 0x20, | ||
79 | 0xff, | ||
80 | 0x40, | ||
81 | 0xff, | ||
82 | 0x80, | ||
83 | 0xff, | ||
84 | }; | ||
85 | |||
86 | for (unsigned y = 0; y < buf.height(); ++y) { | ||
87 | unsigned int bcol = colors[y * ARRAY_SIZE(colors) / buf.height()]; | ||
88 | uint8_t *line = (uint8_t*)(buf.map(0) + buf.stride(0) * y); | ||
89 | |||
90 | if (old_xpos >= 0) { | ||
91 | for (int x = old_xpos; x < old_xpos + width; ++x) | ||
92 | line[x] = 0; | ||
93 | } | ||
94 | |||
95 | for (int x = xpos; x < xpos + width; ++x) | ||
96 | line[x] = bcol; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | void draw_color_bar(IFramebuffer& buf, int old_xpos, int xpos, int width) | ||
101 | { | ||
102 | switch (buf.format()) { | ||
103 | case PixelFormat::NV12: | ||
104 | case PixelFormat::NV21: | ||
105 | // XXX not right but gets something on the screen | ||
106 | drm_draw_color_bar_semiplanar_yuv(buf, old_xpos, xpos, width); | ||
107 | break; | ||
108 | |||
109 | case PixelFormat::YUYV: | ||
110 | case PixelFormat::UYVY: | ||
111 | // XXX not right but gets something on the screen | ||
112 | drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); | ||
113 | break; | ||
114 | |||
115 | case PixelFormat::RGB565: | ||
116 | drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); | ||
117 | break; | ||
118 | |||
119 | case PixelFormat::BGR565: | ||
120 | // XXX not right, red and blue are reversed | ||
121 | drm_draw_color_bar_rgb565(buf, old_xpos, xpos, width); | ||
122 | break; | ||
123 | |||
124 | case PixelFormat::XRGB8888: | ||
125 | drm_draw_color_bar_rgb888(buf, old_xpos, xpos, width); | ||
126 | break; | ||
127 | |||
128 | default: | ||
129 | ASSERT(false); | ||
130 | } | ||
131 | } | ||
132 | } | ||
diff --git a/kms++util/src/cpuframebuffer.cpp b/kms++util/src/cpuframebuffer.cpp new file mode 100644 index 0000000..d356596 --- /dev/null +++ b/kms++util/src/cpuframebuffer.cpp | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <map> | ||
2 | |||
3 | #include <kms++util/cpuframebuffer.h> | ||
4 | |||
5 | using namespace std; | ||
6 | |||
7 | namespace kms { | ||
8 | |||
9 | CPUFramebuffer::CPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format) | ||
10 | : m_width(width), m_height(height), m_format(format) | ||
11 | { | ||
12 | const PixelFormatInfo& format_info = get_pixel_format_info(m_format); | ||
13 | |||
14 | m_num_planes = format_info.num_planes; | ||
15 | |||
16 | for (unsigned i = 0; i < format_info.num_planes; ++i) { | ||
17 | const PixelFormatPlaneInfo& pi = format_info.planes[i]; | ||
18 | FramebufferPlane& plane = m_planes[i]; | ||
19 | |||
20 | plane.stride = width * pi.bitspp / 8; | ||
21 | plane.size = plane.stride * height/ pi.ysub; | ||
22 | plane.offset = 0; | ||
23 | plane.map = new uint8_t[plane.size]; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | CPUFramebuffer::~CPUFramebuffer() | ||
28 | { | ||
29 | for (unsigned i = 0; i < m_num_planes; ++i) { | ||
30 | FramebufferPlane& plane = m_planes[i]; | ||
31 | |||
32 | delete plane.map; | ||
33 | } | ||
34 | } | ||
35 | |||
36 | } | ||
diff --git a/kms++util/src/drawing.cpp b/kms++util/src/drawing.cpp new file mode 100644 index 0000000..4e5c6c1 --- /dev/null +++ b/kms++util/src/drawing.cpp | |||
@@ -0,0 +1,275 @@ | |||
1 | |||
2 | #include <kms++/kms++.h> | ||
3 | #include <kms++util/kms++util.h> | ||
4 | |||
5 | using namespace std; | ||
6 | |||
7 | namespace kms | ||
8 | { | ||
9 | void draw_rgb_pixel(IFramebuffer& buf, unsigned x, unsigned y, RGB color) | ||
10 | { | ||
11 | if (x >= buf.width() || y >= buf.height()) | ||
12 | throw runtime_error("attempt to draw outside the buffer"); | ||
13 | |||
14 | switch (buf.format()) { | ||
15 | case PixelFormat::XRGB8888: | ||
16 | case PixelFormat::ARGB8888: | ||
17 | { | ||
18 | uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4); | ||
19 | *p = color.argb8888(); | ||
20 | break; | ||
21 | } | ||
22 | case PixelFormat::XBGR8888: | ||
23 | case PixelFormat::ABGR8888: | ||
24 | { | ||
25 | uint32_t *p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4); | ||
26 | *p = color.abgr8888(); | ||
27 | break; | ||
28 | } | ||
29 | case PixelFormat::RGB888: | ||
30 | { | ||
31 | uint8_t *p = buf.map(0) + buf.stride(0) * y + x * 3; | ||
32 | p[0] = color.b; | ||
33 | p[1] = color.g; | ||
34 | p[2] = color.r; | ||
35 | break; | ||
36 | } | ||
37 | case PixelFormat::BGR888: | ||
38 | { | ||
39 | uint8_t *p = buf.map(0) + buf.stride(0) * y + x * 3; | ||
40 | p[0] = color.r; | ||
41 | p[1] = color.g; | ||
42 | p[2] = color.b; | ||
43 | break; | ||
44 | } | ||
45 | case PixelFormat::RGB565: | ||
46 | { | ||
47 | uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2); | ||
48 | *p = color.rgb565(); | ||
49 | break; | ||
50 | } | ||
51 | case PixelFormat::BGR565: | ||
52 | { | ||
53 | uint16_t *p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2); | ||
54 | *p = color.bgr565(); | ||
55 | break; | ||
56 | } | ||
57 | default: | ||
58 | throw std::invalid_argument("invalid pixelformat"); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2) | ||
63 | { | ||
64 | if ((x + 1) >= buf.width() || y >= buf.height()) | ||
65 | throw runtime_error("attempt to draw outside the buffer"); | ||
66 | |||
67 | ASSERT((x & 1) == 0); | ||
68 | |||
69 | uint8_t *p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2); | ||
70 | |||
71 | uint8_t y0 = yuv1.y; | ||
72 | uint8_t y1 = yuv2.y; | ||
73 | uint8_t u = (yuv1.u + yuv2.u) / 2; | ||
74 | uint8_t v = (yuv1.v + yuv2.v) / 2; | ||
75 | |||
76 | switch (buf.format()) { | ||
77 | case PixelFormat::UYVY: | ||
78 | p[0] = u; | ||
79 | p[1] = y0; | ||
80 | p[2] = v; | ||
81 | p[3] = y1; | ||
82 | break; | ||
83 | |||
84 | case PixelFormat::YUYV: | ||
85 | p[0] = y0; | ||
86 | p[1] = u; | ||
87 | p[2] = y1; | ||
88 | p[3] = v; | ||
89 | break; | ||
90 | |||
91 | case PixelFormat::YVYU: | ||
92 | p[0] = y0; | ||
93 | p[1] = v; | ||
94 | p[2] = y1; | ||
95 | p[3] = u; | ||
96 | break; | ||
97 | |||
98 | case PixelFormat::VYUY: | ||
99 | p[0] = v; | ||
100 | p[1] = y0; | ||
101 | p[2] = u; | ||
102 | p[3] = y1; | ||
103 | break; | ||
104 | |||
105 | default: | ||
106 | throw std::invalid_argument("invalid pixelformat"); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | void draw_yuv420_macropixel(IFramebuffer& buf, unsigned x, unsigned y, | ||
111 | YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4) | ||
112 | { | ||
113 | if ((x + 1) >= buf.width() || (y + 1) >= buf.height()) | ||
114 | throw runtime_error("attempt to draw outside the buffer"); | ||
115 | |||
116 | ASSERT((x & 1) == 0); | ||
117 | ASSERT((y & 1) == 0); | ||
118 | |||
119 | uint8_t *py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x); | ||
120 | uint8_t *py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x); | ||
121 | |||
122 | uint8_t *puv = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x); | ||
123 | |||
124 | uint8_t y0 = yuv1.y; | ||
125 | uint8_t y1 = yuv2.y; | ||
126 | uint8_t y2 = yuv3.y; | ||
127 | uint8_t y3 = yuv4.y; | ||
128 | uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4; | ||
129 | uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4; | ||
130 | |||
131 | switch (buf.format()) { | ||
132 | case PixelFormat::NV12: | ||
133 | py1[0] = y0; | ||
134 | py1[1] = y1; | ||
135 | py2[0] = y2; | ||
136 | py2[1] = y3; | ||
137 | puv[0] = u; | ||
138 | puv[1] = v; | ||
139 | break; | ||
140 | |||
141 | case PixelFormat::NV21: | ||
142 | py1[0] = y0; | ||
143 | py1[1] = y1; | ||
144 | py2[0] = y2; | ||
145 | py2[1] = y3; | ||
146 | puv[0] = v; | ||
147 | puv[1] = u; | ||
148 | break; | ||
149 | |||
150 | default: | ||
151 | throw std::invalid_argument("invalid pixelformat"); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | void draw_rect(IFramebuffer &fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color) | ||
156 | { | ||
157 | unsigned i, j; | ||
158 | YUV yuvcolor = color.yuv(); | ||
159 | |||
160 | switch (fb.format()) { | ||
161 | case PixelFormat::XRGB8888: | ||
162 | case PixelFormat::XBGR8888: | ||
163 | case PixelFormat::ARGB8888: | ||
164 | case PixelFormat::ABGR8888: | ||
165 | case PixelFormat::RGB888: | ||
166 | case PixelFormat::BGR888: | ||
167 | case PixelFormat::RGB565: | ||
168 | case PixelFormat::BGR565: | ||
169 | for (j = 0; j < h; j++) { | ||
170 | for (i = 0; i < w; i++) { | ||
171 | draw_rgb_pixel(fb, x + i, y + j, color); | ||
172 | } | ||
173 | } | ||
174 | break; | ||
175 | |||
176 | case PixelFormat::UYVY: | ||
177 | case PixelFormat::YUYV: | ||
178 | case PixelFormat::YVYU: | ||
179 | case PixelFormat::VYUY: | ||
180 | for (j = 0; j < h; j++) { | ||
181 | for (i = 0; i < w; i += 2) { | ||
182 | draw_yuv422_macropixel(fb, x + i, y + j, yuvcolor, yuvcolor); | ||
183 | } | ||
184 | } | ||
185 | break; | ||
186 | |||
187 | case PixelFormat::NV12: | ||
188 | case PixelFormat::NV21: | ||
189 | for (j = 0; j < h; j += 2) { | ||
190 | for (i = 0; i < w; i += 2) { | ||
191 | draw_yuv420_macropixel(fb, x + i, y + j, | ||
192 | yuvcolor, yuvcolor, yuvcolor, yuvcolor); | ||
193 | } | ||
194 | } | ||
195 | break; | ||
196 | default: | ||
197 | throw std::invalid_argument("unknown pixelformat"); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | static bool get_char_pixel(char c, uint32_t x, uint32_t y) | ||
202 | { | ||
203 | #include "font_8x8.h" | ||
204 | |||
205 | uint8_t bits = fontdata_8x8[8 * c + y]; | ||
206 | bool bit = (bits >> (7 - x)) & 1; | ||
207 | |||
208 | return bit; | ||
209 | } | ||
210 | |||
211 | static void draw_char(IFramebuffer& buf, uint32_t xpos, uint32_t ypos, char c, RGB color) | ||
212 | { | ||
213 | unsigned x, y; | ||
214 | YUV yuvcolor = color.yuv(); | ||
215 | |||
216 | switch (buf.format()) { | ||
217 | case PixelFormat::XRGB8888: | ||
218 | case PixelFormat::XBGR8888: | ||
219 | case PixelFormat::ARGB8888: | ||
220 | case PixelFormat::ABGR8888: | ||
221 | case PixelFormat::RGB888: | ||
222 | case PixelFormat::BGR888: | ||
223 | case PixelFormat::RGB565: | ||
224 | case PixelFormat::BGR565: | ||
225 | for (y = 0; y < 8; y++) { | ||
226 | for (x = 0; x < 8; x++) { | ||
227 | bool b = get_char_pixel(c, x, y); | ||
228 | |||
229 | draw_rgb_pixel(buf, xpos + x, ypos + y, b ? color : RGB()); | ||
230 | } | ||
231 | } | ||
232 | break; | ||
233 | |||
234 | case PixelFormat::UYVY: | ||
235 | case PixelFormat::YUYV: | ||
236 | case PixelFormat::YVYU: | ||
237 | case PixelFormat::VYUY: | ||
238 | for (y = 0; y < 8; y++) { | ||
239 | for (x = 0; x < 8; x += 2) { | ||
240 | bool b0 = get_char_pixel(c, x, y); | ||
241 | bool b1 = get_char_pixel(c, x + 1, y); | ||
242 | |||
243 | draw_yuv422_macropixel(buf, xpos + x, ypos + y, | ||
244 | b0 ? yuvcolor : YUV(RGB()), b1 ? yuvcolor : YUV(RGB())); | ||
245 | } | ||
246 | } | ||
247 | break; | ||
248 | |||
249 | case PixelFormat::NV12: | ||
250 | case PixelFormat::NV21: | ||
251 | for (y = 0; y < 8; y += 2) { | ||
252 | for (x = 0; x < 8; x += 2) { | ||
253 | bool b00 = get_char_pixel(c, x, y); | ||
254 | bool b10 = get_char_pixel(c, x + 1, y); | ||
255 | bool b01 = get_char_pixel(c, x, y + 1); | ||
256 | bool b11 = get_char_pixel(c, x + 1, y + 1); | ||
257 | |||
258 | draw_yuv420_macropixel(buf, xpos + x, ypos + y, | ||
259 | b00 ? yuvcolor : YUV(RGB()), b10 ? yuvcolor : YUV(RGB()), | ||
260 | b01 ? yuvcolor : YUV(RGB()), b11 ? yuvcolor : YUV(RGB())); | ||
261 | } | ||
262 | } | ||
263 | break; | ||
264 | default: | ||
265 | throw std::invalid_argument("unknown pixelformat"); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const string& str, RGB color) | ||
270 | { | ||
271 | for(unsigned i = 0; i < str.size(); i++) | ||
272 | draw_char(buf, (x + 8 * i), y, str[i], color); | ||
273 | } | ||
274 | |||
275 | } | ||
diff --git a/kms++util/src/extcpuframebuffer.cpp b/kms++util/src/extcpuframebuffer.cpp new file mode 100644 index 0000000..feb3add --- /dev/null +++ b/kms++util/src/extcpuframebuffer.cpp | |||
@@ -0,0 +1,49 @@ | |||
1 | |||
2 | #include <kms++util/kms++util.h> | ||
3 | |||
4 | using namespace std; | ||
5 | |||
6 | namespace kms | ||
7 | { | ||
8 | |||
9 | ExtCPUFramebuffer::ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, | ||
10 | uint8_t* buffer, uint32_t size, uint32_t pitch, uint32_t offset) | ||
11 | : m_width(width), m_height(height), m_format(format) | ||
12 | { | ||
13 | const PixelFormatInfo& format_info = get_pixel_format_info(m_format); | ||
14 | |||
15 | m_num_planes = format_info.num_planes; | ||
16 | |||
17 | ASSERT(m_num_planes == 1); | ||
18 | |||
19 | FramebufferPlane& plane = m_planes[0]; | ||
20 | |||
21 | plane.stride = pitch; | ||
22 | plane.size = size; | ||
23 | plane.offset = offset; | ||
24 | plane.map = buffer; | ||
25 | } | ||
26 | |||
27 | ExtCPUFramebuffer::ExtCPUFramebuffer(uint32_t width, uint32_t height, PixelFormat format, | ||
28 | uint8_t* buffers[4], uint32_t sizes[4], uint32_t pitches[4], uint32_t offsets[4]) | ||
29 | : m_width(width), m_height(height), m_format(format) | ||
30 | { | ||
31 | const PixelFormatInfo& format_info = get_pixel_format_info(m_format); | ||
32 | |||
33 | m_num_planes = format_info.num_planes; | ||
34 | |||
35 | for (unsigned i = 0; i < format_info.num_planes; ++i) { | ||
36 | FramebufferPlane& plane = m_planes[i]; | ||
37 | |||
38 | plane.stride = pitches[i]; | ||
39 | plane.size = sizes[i]; | ||
40 | plane.offset = offsets[i]; | ||
41 | plane.map = buffers[i]; | ||
42 | } | ||
43 | } | ||
44 | |||
45 | ExtCPUFramebuffer::~ExtCPUFramebuffer() | ||
46 | { | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/kms++util/src/font_8x8.h b/kms++util/src/font_8x8.h new file mode 100644 index 0000000..2a2a1ea --- /dev/null +++ b/kms++util/src/font_8x8.h | |||
@@ -0,0 +1,2570 @@ | |||
1 | /**********************************************/ | ||
2 | /* */ | ||
3 | /* Font file generated by cpi2fnt */ | ||
4 | /* */ | ||
5 | /**********************************************/ | ||
6 | |||
7 | const uint8_t fontdata_8x8[] = { | ||
8 | |||
9 | /* 0 0x00 '^@' */ | ||
10 | 0x00, /* 00000000 */ | ||
11 | 0x00, /* 00000000 */ | ||
12 | 0x00, /* 00000000 */ | ||
13 | 0x00, /* 00000000 */ | ||
14 | 0x00, /* 00000000 */ | ||
15 | 0x00, /* 00000000 */ | ||
16 | 0x00, /* 00000000 */ | ||
17 | 0x00, /* 00000000 */ | ||
18 | |||
19 | /* 1 0x01 '^A' */ | ||
20 | 0x7e, /* 01111110 */ | ||
21 | 0x81, /* 10000001 */ | ||
22 | 0xa5, /* 10100101 */ | ||
23 | 0x81, /* 10000001 */ | ||
24 | 0xbd, /* 10111101 */ | ||
25 | 0x99, /* 10011001 */ | ||
26 | 0x81, /* 10000001 */ | ||
27 | 0x7e, /* 01111110 */ | ||
28 | |||
29 | /* 2 0x02 '^B' */ | ||
30 | 0x7e, /* 01111110 */ | ||
31 | 0xff, /* 11111111 */ | ||
32 | 0xdb, /* 11011011 */ | ||
33 | 0xff, /* 11111111 */ | ||
34 | 0xc3, /* 11000011 */ | ||
35 | 0xe7, /* 11100111 */ | ||
36 | 0xff, /* 11111111 */ | ||
37 | 0x7e, /* 01111110 */ | ||
38 | |||
39 | /* 3 0x03 '^C' */ | ||
40 | 0x6c, /* 01101100 */ | ||
41 | 0xfe, /* 11111110 */ | ||
42 | 0xfe, /* 11111110 */ | ||
43 | 0xfe, /* 11111110 */ | ||
44 | 0x7c, /* 01111100 */ | ||
45 | 0x38, /* 00111000 */ | ||
46 | 0x10, /* 00010000 */ | ||
47 | 0x00, /* 00000000 */ | ||
48 | |||
49 | /* 4 0x04 '^D' */ | ||
50 | 0x10, /* 00010000 */ | ||
51 | 0x38, /* 00111000 */ | ||
52 | 0x7c, /* 01111100 */ | ||
53 | 0xfe, /* 11111110 */ | ||
54 | 0x7c, /* 01111100 */ | ||
55 | 0x38, /* 00111000 */ | ||
56 | 0x10, /* 00010000 */ | ||
57 | 0x00, /* 00000000 */ | ||
58 | |||
59 | /* 5 0x05 '^E' */ | ||
60 | 0x38, /* 00111000 */ | ||
61 | 0x7c, /* 01111100 */ | ||
62 | 0x38, /* 00111000 */ | ||
63 | 0xfe, /* 11111110 */ | ||
64 | 0xfe, /* 11111110 */ | ||
65 | 0xd6, /* 11010110 */ | ||
66 | 0x10, /* 00010000 */ | ||
67 | 0x38, /* 00111000 */ | ||
68 | |||
69 | /* 6 0x06 '^F' */ | ||
70 | 0x10, /* 00010000 */ | ||
71 | 0x38, /* 00111000 */ | ||
72 | 0x7c, /* 01111100 */ | ||
73 | 0xfe, /* 11111110 */ | ||
74 | 0xfe, /* 11111110 */ | ||
75 | 0x7c, /* 01111100 */ | ||
76 | 0x10, /* 00010000 */ | ||
77 | 0x38, /* 00111000 */ | ||
78 | |||
79 | /* 7 0x07 '^G' */ | ||
80 | 0x00, /* 00000000 */ | ||
81 | 0x00, /* 00000000 */ | ||
82 | 0x18, /* 00011000 */ | ||
83 | 0x3c, /* 00111100 */ | ||
84 | 0x3c, /* 00111100 */ | ||
85 | 0x18, /* 00011000 */ | ||
86 | 0x00, /* 00000000 */ | ||
87 | 0x00, /* 00000000 */ | ||
88 | |||
89 | /* 8 0x08 '^H' */ | ||
90 | 0xff, /* 11111111 */ | ||
91 | 0xff, /* 11111111 */ | ||
92 | 0xe7, /* 11100111 */ | ||
93 | 0xc3, /* 11000011 */ | ||
94 | 0xc3, /* 11000011 */ | ||
95 | 0xe7, /* 11100111 */ | ||
96 | 0xff, /* 11111111 */ | ||
97 | 0xff, /* 11111111 */ | ||
98 | |||
99 | /* 9 0x09 '^I' */ | ||
100 | 0x00, /* 00000000 */ | ||
101 | 0x3c, /* 00111100 */ | ||
102 | 0x66, /* 01100110 */ | ||
103 | 0x42, /* 01000010 */ | ||
104 | 0x42, /* 01000010 */ | ||
105 | 0x66, /* 01100110 */ | ||
106 | 0x3c, /* 00111100 */ | ||
107 | 0x00, /* 00000000 */ | ||
108 | |||
109 | /* 10 0x0a '^J' */ | ||
110 | 0xff, /* 11111111 */ | ||
111 | 0xc3, /* 11000011 */ | ||
112 | 0x99, /* 10011001 */ | ||
113 | 0xbd, /* 10111101 */ | ||
114 | 0xbd, /* 10111101 */ | ||
115 | 0x99, /* 10011001 */ | ||
116 | 0xc3, /* 11000011 */ | ||
117 | 0xff, /* 11111111 */ | ||
118 | |||
119 | /* 11 0x0b '^K' */ | ||
120 | 0x0f, /* 00001111 */ | ||
121 | 0x07, /* 00000111 */ | ||
122 | 0x0f, /* 00001111 */ | ||
123 | 0x7d, /* 01111101 */ | ||
124 | 0xcc, /* 11001100 */ | ||
125 | 0xcc, /* 11001100 */ | ||
126 | 0xcc, /* 11001100 */ | ||
127 | 0x78, /* 01111000 */ | ||
128 | |||
129 | /* 12 0x0c '^L' */ | ||
130 | 0x3c, /* 00111100 */ | ||
131 | 0x66, /* 01100110 */ | ||
132 | 0x66, /* 01100110 */ | ||
133 | 0x66, /* 01100110 */ | ||
134 | 0x3c, /* 00111100 */ | ||
135 | 0x18, /* 00011000 */ | ||
136 | 0x7e, /* 01111110 */ | ||
137 | 0x18, /* 00011000 */ | ||
138 | |||
139 | /* 13 0x0d '^M' */ | ||
140 | 0x3f, /* 00111111 */ | ||
141 | 0x33, /* 00110011 */ | ||
142 | 0x3f, /* 00111111 */ | ||
143 | 0x30, /* 00110000 */ | ||
144 | 0x30, /* 00110000 */ | ||
145 | 0x70, /* 01110000 */ | ||
146 | 0xf0, /* 11110000 */ | ||
147 | 0xe0, /* 11100000 */ | ||
148 | |||
149 | /* 14 0x0e '^N' */ | ||
150 | 0x7f, /* 01111111 */ | ||
151 | 0x63, /* 01100011 */ | ||
152 | 0x7f, /* 01111111 */ | ||
153 | 0x63, /* 01100011 */ | ||
154 | 0x63, /* 01100011 */ | ||
155 | 0x67, /* 01100111 */ | ||
156 | 0xe6, /* 11100110 */ | ||
157 | 0xc0, /* 11000000 */ | ||
158 | |||
159 | /* 15 0x0f '^O' */ | ||
160 | 0x18, /* 00011000 */ | ||
161 | 0xdb, /* 11011011 */ | ||
162 | 0x3c, /* 00111100 */ | ||
163 | 0xe7, /* 11100111 */ | ||
164 | 0xe7, /* 11100111 */ | ||
165 | 0x3c, /* 00111100 */ | ||
166 | 0xdb, /* 11011011 */ | ||
167 | 0x18, /* 00011000 */ | ||
168 | |||
169 | /* 16 0x10 '^P' */ | ||
170 | 0x80, /* 10000000 */ | ||
171 | 0xe0, /* 11100000 */ | ||
172 | 0xf8, /* 11111000 */ | ||
173 | 0xfe, /* 11111110 */ | ||
174 | 0xf8, /* 11111000 */ | ||
175 | 0xe0, /* 11100000 */ | ||
176 | 0x80, /* 10000000 */ | ||
177 | 0x00, /* 00000000 */ | ||
178 | |||
179 | /* 17 0x11 '^Q' */ | ||
180 | 0x02, /* 00000010 */ | ||
181 | 0x0e, /* 00001110 */ | ||
182 | 0x3e, /* 00111110 */ | ||
183 | 0xfe, /* 11111110 */ | ||
184 | 0x3e, /* 00111110 */ | ||
185 | 0x0e, /* 00001110 */ | ||
186 | 0x02, /* 00000010 */ | ||
187 | 0x00, /* 00000000 */ | ||
188 | |||
189 | /* 18 0x12 '^R' */ | ||
190 | 0x18, /* 00011000 */ | ||
191 | 0x3c, /* 00111100 */ | ||
192 | 0x7e, /* 01111110 */ | ||
193 | 0x18, /* 00011000 */ | ||
194 | 0x18, /* 00011000 */ | ||
195 | 0x7e, /* 01111110 */ | ||
196 | 0x3c, /* 00111100 */ | ||
197 | 0x18, /* 00011000 */ | ||
198 | |||
199 | /* 19 0x13 '^S' */ | ||
200 | 0x66, /* 01100110 */ | ||
201 | 0x66, /* 01100110 */ | ||
202 | 0x66, /* 01100110 */ | ||
203 | 0x66, /* 01100110 */ | ||
204 | 0x66, /* 01100110 */ | ||
205 | 0x00, /* 00000000 */ | ||
206 | 0x66, /* 01100110 */ | ||
207 | 0x00, /* 00000000 */ | ||
208 | |||
209 | /* 20 0x14 '^T' */ | ||
210 | 0x7f, /* 01111111 */ | ||
211 | 0xdb, /* 11011011 */ | ||
212 | 0xdb, /* 11011011 */ | ||
213 | 0x7b, /* 01111011 */ | ||
214 | 0x1b, /* 00011011 */ | ||
215 | 0x1b, /* 00011011 */ | ||
216 | 0x1b, /* 00011011 */ | ||
217 | 0x00, /* 00000000 */ | ||
218 | |||
219 | /* 21 0x15 '^U' */ | ||
220 | 0x3e, /* 00111110 */ | ||
221 | 0x61, /* 01100001 */ | ||
222 | 0x3c, /* 00111100 */ | ||
223 | 0x66, /* 01100110 */ | ||
224 | 0x66, /* 01100110 */ | ||
225 | 0x3c, /* 00111100 */ | ||
226 | 0x86, /* 10000110 */ | ||
227 | 0x7c, /* 01111100 */ | ||
228 | |||
229 | /* 22 0x16 '^V' */ | ||
230 | 0x00, /* 00000000 */ | ||
231 | 0x00, /* 00000000 */ | ||
232 | 0x00, /* 00000000 */ | ||
233 | 0x00, /* 00000000 */ | ||
234 | 0x7e, /* 01111110 */ | ||
235 | 0x7e, /* 01111110 */ | ||
236 | 0x7e, /* 01111110 */ | ||
237 | 0x00, /* 00000000 */ | ||
238 | |||
239 | /* 23 0x17 '^W' */ | ||
240 | 0x18, /* 00011000 */ | ||
241 | 0x3c, /* 00111100 */ | ||
242 | 0x7e, /* 01111110 */ | ||
243 | 0x18, /* 00011000 */ | ||
244 | 0x7e, /* 01111110 */ | ||
245 | 0x3c, /* 00111100 */ | ||
246 | 0x18, /* 00011000 */ | ||
247 | 0xff, /* 11111111 */ | ||
248 | |||
249 | /* 24 0x18 '^X' */ | ||
250 | 0x18, /* 00011000 */ | ||
251 | 0x3c, /* 00111100 */ | ||
252 | 0x7e, /* 01111110 */ | ||
253 | 0x18, /* 00011000 */ | ||
254 | 0x18, /* 00011000 */ | ||
255 | 0x18, /* 00011000 */ | ||
256 | 0x18, /* 00011000 */ | ||
257 | 0x00, /* 00000000 */ | ||
258 | |||
259 | /* 25 0x19 '^Y' */ | ||
260 | 0x18, /* 00011000 */ | ||
261 | 0x18, /* 00011000 */ | ||
262 | 0x18, /* 00011000 */ | ||
263 | 0x18, /* 00011000 */ | ||
264 | 0x7e, /* 01111110 */ | ||
265 | 0x3c, /* 00111100 */ | ||
266 | 0x18, /* 00011000 */ | ||
267 | 0x00, /* 00000000 */ | ||
268 | |||
269 | /* 26 0x1a '^Z' */ | ||
270 | 0x00, /* 00000000 */ | ||
271 | 0x18, /* 00011000 */ | ||
272 | 0x0c, /* 00001100 */ | ||
273 | 0xfe, /* 11111110 */ | ||
274 | 0x0c, /* 00001100 */ | ||
275 | 0x18, /* 00011000 */ | ||
276 | 0x00, /* 00000000 */ | ||
277 | 0x00, /* 00000000 */ | ||
278 | |||
279 | /* 27 0x1b '^[' */ | ||
280 | 0x00, /* 00000000 */ | ||
281 | 0x30, /* 00110000 */ | ||
282 | 0x60, /* 01100000 */ | ||
283 | 0xfe, /* 11111110 */ | ||
284 | 0x60, /* 01100000 */ | ||
285 | 0x30, /* 00110000 */ | ||
286 | 0x00, /* 00000000 */ | ||
287 | 0x00, /* 00000000 */ | ||
288 | |||
289 | /* 28 0x1c '^\' */ | ||
290 | 0x00, /* 00000000 */ | ||
291 | 0x00, /* 00000000 */ | ||
292 | 0xc0, /* 11000000 */ | ||
293 | 0xc0, /* 11000000 */ | ||
294 | 0xc0, /* 11000000 */ | ||
295 | 0xfe, /* 11111110 */ | ||
296 | 0x00, /* 00000000 */ | ||
297 | 0x00, /* 00000000 */ | ||
298 | |||
299 | /* 29 0x1d '^]' */ | ||
300 | 0x00, /* 00000000 */ | ||
301 | 0x24, /* 00100100 */ | ||
302 | 0x66, /* 01100110 */ | ||
303 | 0xff, /* 11111111 */ | ||
304 | 0x66, /* 01100110 */ | ||
305 | 0x24, /* 00100100 */ | ||
306 | 0x00, /* 00000000 */ | ||
307 | 0x00, /* 00000000 */ | ||
308 | |||
309 | /* 30 0x1e '^^' */ | ||
310 | 0x00, /* 00000000 */ | ||
311 | 0x18, /* 00011000 */ | ||
312 | 0x3c, /* 00111100 */ | ||
313 | 0x7e, /* 01111110 */ | ||
314 | 0xff, /* 11111111 */ | ||
315 | 0xff, /* 11111111 */ | ||
316 | 0x00, /* 00000000 */ | ||
317 | 0x00, /* 00000000 */ | ||
318 | |||
319 | /* 31 0x1f '^_' */ | ||
320 | 0x00, /* 00000000 */ | ||
321 | 0xff, /* 11111111 */ | ||
322 | 0xff, /* 11111111 */ | ||
323 | 0x7e, /* 01111110 */ | ||
324 | 0x3c, /* 00111100 */ | ||
325 | 0x18, /* 00011000 */ | ||
326 | 0x00, /* 00000000 */ | ||
327 | 0x00, /* 00000000 */ | ||
328 | |||
329 | /* 32 0x20 ' ' */ | ||
330 | 0x00, /* 00000000 */ | ||
331 | 0x00, /* 00000000 */ | ||
332 | 0x00, /* 00000000 */ | ||
333 | 0x00, /* 00000000 */ | ||
334 | 0x00, /* 00000000 */ | ||
335 | 0x00, /* 00000000 */ | ||
336 | 0x00, /* 00000000 */ | ||
337 | 0x00, /* 00000000 */ | ||
338 | |||
339 | /* 33 0x21 '!' */ | ||
340 | 0x18, /* 00011000 */ | ||
341 | 0x3c, /* 00111100 */ | ||
342 | 0x3c, /* 00111100 */ | ||
343 | 0x18, /* 00011000 */ | ||
344 | 0x18, /* 00011000 */ | ||
345 | 0x00, /* 00000000 */ | ||
346 | 0x18, /* 00011000 */ | ||
347 | 0x00, /* 00000000 */ | ||
348 | |||
349 | /* 34 0x22 '"' */ | ||
350 | 0x66, /* 01100110 */ | ||
351 | 0x66, /* 01100110 */ | ||
352 | 0x24, /* 00100100 */ | ||
353 | 0x00, /* 00000000 */ | ||
354 | 0x00, /* 00000000 */ | ||
355 | 0x00, /* 00000000 */ | ||
356 | 0x00, /* 00000000 */ | ||
357 | 0x00, /* 00000000 */ | ||
358 | |||
359 | /* 35 0x23 '#' */ | ||
360 | 0x6c, /* 01101100 */ | ||
361 | 0x6c, /* 01101100 */ | ||
362 | 0xfe, /* 11111110 */ | ||
363 | 0x6c, /* 01101100 */ | ||
364 | 0xfe, /* 11111110 */ | ||
365 | 0x6c, /* 01101100 */ | ||
366 | 0x6c, /* 01101100 */ | ||
367 | 0x00, /* 00000000 */ | ||
368 | |||
369 | /* 36 0x24 '$' */ | ||
370 | 0x18, /* 00011000 */ | ||
371 | 0x3e, /* 00111110 */ | ||
372 | 0x60, /* 01100000 */ | ||
373 | 0x3c, /* 00111100 */ | ||
374 | 0x06, /* 00000110 */ | ||
375 | 0x7c, /* 01111100 */ | ||
376 | 0x18, /* 00011000 */ | ||
377 | 0x00, /* 00000000 */ | ||
378 | |||
379 | /* 37 0x25 '%' */ | ||
380 | 0x00, /* 00000000 */ | ||
381 | 0xc6, /* 11000110 */ | ||
382 | 0xcc, /* 11001100 */ | ||
383 | 0x18, /* 00011000 */ | ||
384 | 0x30, /* 00110000 */ | ||
385 | 0x66, /* 01100110 */ | ||
386 | 0xc6, /* 11000110 */ | ||
387 | 0x00, /* 00000000 */ | ||
388 | |||
389 | /* 38 0x26 '&' */ | ||
390 | 0x38, /* 00111000 */ | ||
391 | 0x6c, /* 01101100 */ | ||
392 | 0x38, /* 00111000 */ | ||
393 | 0x76, /* 01110110 */ | ||
394 | 0xdc, /* 11011100 */ | ||
395 | 0xcc, /* 11001100 */ | ||
396 | 0x76, /* 01110110 */ | ||
397 | 0x00, /* 00000000 */ | ||
398 | |||
399 | /* 39 0x27 ''' */ | ||
400 | 0x18, /* 00011000 */ | ||
401 | 0x18, /* 00011000 */ | ||
402 | 0x30, /* 00110000 */ | ||
403 | 0x00, /* 00000000 */ | ||
404 | 0x00, /* 00000000 */ | ||
405 | 0x00, /* 00000000 */ | ||
406 | 0x00, /* 00000000 */ | ||
407 | 0x00, /* 00000000 */ | ||
408 | |||
409 | /* 40 0x28 '(' */ | ||
410 | 0x0c, /* 00001100 */ | ||
411 | 0x18, /* 00011000 */ | ||
412 | 0x30, /* 00110000 */ | ||
413 | 0x30, /* 00110000 */ | ||
414 | 0x30, /* 00110000 */ | ||
415 | 0x18, /* 00011000 */ | ||
416 | 0x0c, /* 00001100 */ | ||
417 | 0x00, /* 00000000 */ | ||
418 | |||
419 | /* 41 0x29 ')' */ | ||
420 | 0x30, /* 00110000 */ | ||
421 | 0x18, /* 00011000 */ | ||
422 | 0x0c, /* 00001100 */ | ||
423 | 0x0c, /* 00001100 */ | ||
424 | 0x0c, /* 00001100 */ | ||
425 | 0x18, /* 00011000 */ | ||
426 | 0x30, /* 00110000 */ | ||
427 | 0x00, /* 00000000 */ | ||
428 | |||
429 | /* 42 0x2a '*' */ | ||
430 | 0x00, /* 00000000 */ | ||
431 | 0x66, /* 01100110 */ | ||
432 | 0x3c, /* 00111100 */ | ||
433 | 0xff, /* 11111111 */ | ||
434 | 0x3c, /* 00111100 */ | ||
435 | 0x66, /* 01100110 */ | ||
436 | 0x00, /* 00000000 */ | ||
437 | 0x00, /* 00000000 */ | ||
438 | |||
439 | /* 43 0x2b '+' */ | ||
440 | 0x00, /* 00000000 */ | ||
441 | 0x18, /* 00011000 */ | ||
442 | 0x18, /* 00011000 */ | ||
443 | 0x7e, /* 01111110 */ | ||
444 | 0x18, /* 00011000 */ | ||
445 | 0x18, /* 00011000 */ | ||
446 | 0x00, /* 00000000 */ | ||
447 | 0x00, /* 00000000 */ | ||
448 | |||
449 | /* 44 0x2c ',' */ | ||
450 | 0x00, /* 00000000 */ | ||
451 | 0x00, /* 00000000 */ | ||
452 | 0x00, /* 00000000 */ | ||
453 | 0x00, /* 00000000 */ | ||
454 | 0x00, /* 00000000 */ | ||
455 | 0x18, /* 00011000 */ | ||
456 | 0x18, /* 00011000 */ | ||
457 | 0x30, /* 00110000 */ | ||
458 | |||
459 | /* 45 0x2d '-' */ | ||
460 | 0x00, /* 00000000 */ | ||
461 | 0x00, /* 00000000 */ | ||
462 | 0x00, /* 00000000 */ | ||
463 | 0x7e, /* 01111110 */ | ||
464 | 0x00, /* 00000000 */ | ||
465 | 0x00, /* 00000000 */ | ||
466 | 0x00, /* 00000000 */ | ||
467 | 0x00, /* 00000000 */ | ||
468 | |||
469 | /* 46 0x2e '.' */ | ||
470 | 0x00, /* 00000000 */ | ||
471 | 0x00, /* 00000000 */ | ||
472 | 0x00, /* 00000000 */ | ||
473 | 0x00, /* 00000000 */ | ||
474 | 0x00, /* 00000000 */ | ||
475 | 0x18, /* 00011000 */ | ||
476 | 0x18, /* 00011000 */ | ||
477 | 0x00, /* 00000000 */ | ||
478 | |||
479 | /* 47 0x2f '/' */ | ||
480 | 0x06, /* 00000110 */ | ||
481 | 0x0c, /* 00001100 */ | ||
482 | 0x18, /* 00011000 */ | ||
483 | 0x30, /* 00110000 */ | ||
484 | 0x60, /* 01100000 */ | ||
485 | 0xc0, /* 11000000 */ | ||
486 | 0x80, /* 10000000 */ | ||
487 | 0x00, /* 00000000 */ | ||
488 | |||
489 | /* 48 0x30 '0' */ | ||
490 | 0x38, /* 00111000 */ | ||
491 | 0x6c, /* 01101100 */ | ||
492 | 0xc6, /* 11000110 */ | ||
493 | 0xd6, /* 11010110 */ | ||
494 | 0xc6, /* 11000110 */ | ||
495 | 0x6c, /* 01101100 */ | ||
496 | 0x38, /* 00111000 */ | ||
497 | 0x00, /* 00000000 */ | ||
498 | |||
499 | /* 49 0x31 '1' */ | ||
500 | 0x18, /* 00011000 */ | ||
501 | 0x38, /* 00111000 */ | ||
502 | 0x18, /* 00011000 */ | ||
503 | 0x18, /* 00011000 */ | ||
504 | 0x18, /* 00011000 */ | ||
505 | 0x18, /* 00011000 */ | ||
506 | 0x7e, /* 01111110 */ | ||
507 | 0x00, /* 00000000 */ | ||
508 | |||
509 | /* 50 0x32 '2' */ | ||
510 | 0x7c, /* 01111100 */ | ||
511 | 0xc6, /* 11000110 */ | ||
512 | 0x06, /* 00000110 */ | ||
513 | 0x1c, /* 00011100 */ | ||
514 | 0x30, /* 00110000 */ | ||
515 | 0x66, /* 01100110 */ | ||
516 | 0xfe, /* 11111110 */ | ||
517 | 0x00, /* 00000000 */ | ||
518 | |||
519 | /* 51 0x33 '3' */ | ||
520 | 0x7c, /* 01111100 */ | ||
521 | 0xc6, /* 11000110 */ | ||
522 | 0x06, /* 00000110 */ | ||
523 | 0x3c, /* 00111100 */ | ||
524 | 0x06, /* 00000110 */ | ||
525 | 0xc6, /* 11000110 */ | ||
526 | 0x7c, /* 01111100 */ | ||
527 | 0x00, /* 00000000 */ | ||
528 | |||
529 | /* 52 0x34 '4' */ | ||
530 | 0x1c, /* 00011100 */ | ||
531 | 0x3c, /* 00111100 */ | ||
532 | 0x6c, /* 01101100 */ | ||
533 | 0xcc, /* 11001100 */ | ||
534 | 0xfe, /* 11111110 */ | ||
535 | 0x0c, /* 00001100 */ | ||
536 | 0x1e, /* 00011110 */ | ||
537 | 0x00, /* 00000000 */ | ||
538 | |||
539 | /* 53 0x35 '5' */ | ||
540 | 0xfe, /* 11111110 */ | ||
541 | 0xc0, /* 11000000 */ | ||
542 | 0xc0, /* 11000000 */ | ||
543 | 0xfc, /* 11111100 */ | ||
544 | 0x06, /* 00000110 */ | ||
545 | 0xc6, /* 11000110 */ | ||
546 | 0x7c, /* 01111100 */ | ||
547 | 0x00, /* 00000000 */ | ||
548 | |||
549 | /* 54 0x36 '6' */ | ||
550 | 0x38, /* 00111000 */ | ||
551 | 0x60, /* 01100000 */ | ||
552 | 0xc0, /* 11000000 */ | ||
553 | 0xfc, /* 11111100 */ | ||
554 | 0xc6, /* 11000110 */ | ||
555 | 0xc6, /* 11000110 */ | ||
556 | 0x7c, /* 01111100 */ | ||
557 | 0x00, /* 00000000 */ | ||
558 | |||
559 | /* 55 0x37 '7' */ | ||
560 | 0xfe, /* 11111110 */ | ||
561 | 0xc6, /* 11000110 */ | ||
562 | 0x0c, /* 00001100 */ | ||
563 | 0x18, /* 00011000 */ | ||
564 | 0x30, /* 00110000 */ | ||
565 | 0x30, /* 00110000 */ | ||
566 | 0x30, /* 00110000 */ | ||
567 | 0x00, /* 00000000 */ | ||
568 | |||
569 | /* 56 0x38 '8' */ | ||
570 | 0x7c, /* 01111100 */ | ||
571 | 0xc6, /* 11000110 */ | ||
572 | 0xc6, /* 11000110 */ | ||
573 | 0x7c, /* 01111100 */ | ||
574 | 0xc6, /* 11000110 */ | ||
575 | 0xc6, /* 11000110 */ | ||
576 | 0x7c, /* 01111100 */ | ||
577 | 0x00, /* 00000000 */ | ||
578 | |||
579 | /* 57 0x39 '9' */ | ||
580 | 0x7c, /* 01111100 */ | ||
581 | 0xc6, /* 11000110 */ | ||
582 | 0xc6, /* 11000110 */ | ||
583 | 0x7e, /* 01111110 */ | ||
584 | 0x06, /* 00000110 */ | ||
585 | 0x0c, /* 00001100 */ | ||
586 | 0x78, /* 01111000 */ | ||
587 | 0x00, /* 00000000 */ | ||
588 | |||
589 | /* 58 0x3a ':' */ | ||
590 | 0x00, /* 00000000 */ | ||
591 | 0x18, /* 00011000 */ | ||
592 | 0x18, /* 00011000 */ | ||
593 | 0x00, /* 00000000 */ | ||
594 | 0x00, /* 00000000 */ | ||
595 | 0x18, /* 00011000 */ | ||
596 | 0x18, /* 00011000 */ | ||
597 | 0x00, /* 00000000 */ | ||
598 | |||
599 | /* 59 0x3b ';' */ | ||
600 | 0x00, /* 00000000 */ | ||
601 | 0x18, /* 00011000 */ | ||
602 | 0x18, /* 00011000 */ | ||
603 | 0x00, /* 00000000 */ | ||
604 | 0x00, /* 00000000 */ | ||
605 | 0x18, /* 00011000 */ | ||
606 | 0x18, /* 00011000 */ | ||
607 | 0x30, /* 00110000 */ | ||
608 | |||
609 | /* 60 0x3c '<' */ | ||
610 | 0x06, /* 00000110 */ | ||
611 | 0x0c, /* 00001100 */ | ||
612 | 0x18, /* 00011000 */ | ||
613 | 0x30, /* 00110000 */ | ||
614 | 0x18, /* 00011000 */ | ||
615 | 0x0c, /* 00001100 */ | ||
616 | 0x06, /* 00000110 */ | ||
617 | 0x00, /* 00000000 */ | ||
618 | |||
619 | /* 61 0x3d '=' */ | ||
620 | 0x00, /* 00000000 */ | ||
621 | 0x00, /* 00000000 */ | ||
622 | 0x7e, /* 01111110 */ | ||
623 | 0x00, /* 00000000 */ | ||
624 | 0x00, /* 00000000 */ | ||
625 | 0x7e, /* 01111110 */ | ||
626 | 0x00, /* 00000000 */ | ||
627 | 0x00, /* 00000000 */ | ||
628 | |||
629 | /* 62 0x3e '>' */ | ||
630 | 0x60, /* 01100000 */ | ||
631 | 0x30, /* 00110000 */ | ||
632 | 0x18, /* 00011000 */ | ||
633 | 0x0c, /* 00001100 */ | ||
634 | 0x18, /* 00011000 */ | ||
635 | 0x30, /* 00110000 */ | ||
636 | 0x60, /* 01100000 */ | ||
637 | 0x00, /* 00000000 */ | ||
638 | |||
639 | /* 63 0x3f '?' */ | ||
640 | 0x7c, /* 01111100 */ | ||
641 | 0xc6, /* 11000110 */ | ||
642 | 0x0c, /* 00001100 */ | ||
643 | 0x18, /* 00011000 */ | ||
644 | 0x18, /* 00011000 */ | ||
645 | 0x00, /* 00000000 */ | ||
646 | 0x18, /* 00011000 */ | ||
647 | 0x00, /* 00000000 */ | ||
648 | |||
649 | /* 64 0x40 '@' */ | ||
650 | 0x7c, /* 01111100 */ | ||
651 | 0xc6, /* 11000110 */ | ||
652 | 0xde, /* 11011110 */ | ||
653 | 0xde, /* 11011110 */ | ||
654 | 0xde, /* 11011110 */ | ||
655 | 0xc0, /* 11000000 */ | ||
656 | 0x78, /* 01111000 */ | ||
657 | 0x00, /* 00000000 */ | ||
658 | |||
659 | /* 65 0x41 'A' */ | ||
660 | 0x38, /* 00111000 */ | ||
661 | 0x6c, /* 01101100 */ | ||
662 | 0xc6, /* 11000110 */ | ||
663 | 0xfe, /* 11111110 */ | ||
664 | 0xc6, /* 11000110 */ | ||
665 | 0xc6, /* 11000110 */ | ||
666 | 0xc6, /* 11000110 */ | ||
667 | 0x00, /* 00000000 */ | ||
668 | |||
669 | /* 66 0x42 'B' */ | ||
670 | 0xfc, /* 11111100 */ | ||
671 | 0x66, /* 01100110 */ | ||
672 | 0x66, /* 01100110 */ | ||
673 | 0x7c, /* 01111100 */ | ||
674 | 0x66, /* 01100110 */ | ||
675 | 0x66, /* 01100110 */ | ||
676 | 0xfc, /* 11111100 */ | ||
677 | 0x00, /* 00000000 */ | ||
678 | |||
679 | /* 67 0x43 'C' */ | ||
680 | 0x3c, /* 00111100 */ | ||
681 | 0x66, /* 01100110 */ | ||
682 | 0xc0, /* 11000000 */ | ||
683 | 0xc0, /* 11000000 */ | ||
684 | 0xc0, /* 11000000 */ | ||
685 | 0x66, /* 01100110 */ | ||
686 | 0x3c, /* 00111100 */ | ||
687 | 0x00, /* 00000000 */ | ||
688 | |||
689 | /* 68 0x44 'D' */ | ||
690 | 0xf8, /* 11111000 */ | ||
691 | 0x6c, /* 01101100 */ | ||
692 | 0x66, /* 01100110 */ | ||
693 | 0x66, /* 01100110 */ | ||
694 | 0x66, /* 01100110 */ | ||
695 | 0x6c, /* 01101100 */ | ||
696 | 0xf8, /* 11111000 */ | ||
697 | 0x00, /* 00000000 */ | ||
698 | |||
699 | /* 69 0x45 'E' */ | ||
700 | 0xfe, /* 11111110 */ | ||
701 | 0x62, /* 01100010 */ | ||
702 | 0x68, /* 01101000 */ | ||
703 | 0x78, /* 01111000 */ | ||
704 | 0x68, /* 01101000 */ | ||
705 | 0x62, /* 01100010 */ | ||
706 | 0xfe, /* 11111110 */ | ||
707 | 0x00, /* 00000000 */ | ||
708 | |||
709 | /* 70 0x46 'F' */ | ||
710 | 0xfe, /* 11111110 */ | ||
711 | 0x62, /* 01100010 */ | ||
712 | 0x68, /* 01101000 */ | ||
713 | 0x78, /* 01111000 */ | ||
714 | 0x68, /* 01101000 */ | ||
715 | 0x60, /* 01100000 */ | ||
716 | 0xf0, /* 11110000 */ | ||
717 | 0x00, /* 00000000 */ | ||
718 | |||
719 | /* 71 0x47 'G' */ | ||
720 | 0x3c, /* 00111100 */ | ||
721 | 0x66, /* 01100110 */ | ||
722 | 0xc0, /* 11000000 */ | ||
723 | 0xc0, /* 11000000 */ | ||
724 | 0xce, /* 11001110 */ | ||
725 | 0x66, /* 01100110 */ | ||
726 | 0x3a, /* 00111010 */ | ||
727 | 0x00, /* 00000000 */ | ||
728 | |||
729 | /* 72 0x48 'H' */ | ||
730 | 0xc6, /* 11000110 */ | ||
731 | 0xc6, /* 11000110 */ | ||
732 | 0xc6, /* 11000110 */ | ||
733 | 0xfe, /* 11111110 */ | ||
734 | 0xc6, /* 11000110 */ | ||
735 | 0xc6, /* 11000110 */ | ||
736 | 0xc6, /* 11000110 */ | ||
737 | 0x00, /* 00000000 */ | ||
738 | |||
739 | /* 73 0x49 'I' */ | ||
740 | 0x3c, /* 00111100 */ | ||
741 | 0x18, /* 00011000 */ | ||
742 | 0x18, /* 00011000 */ | ||
743 | 0x18, /* 00011000 */ | ||
744 | 0x18, /* 00011000 */ | ||
745 | 0x18, /* 00011000 */ | ||
746 | 0x3c, /* 00111100 */ | ||
747 | 0x00, /* 00000000 */ | ||
748 | |||
749 | /* 74 0x4a 'J' */ | ||
750 | 0x1e, /* 00011110 */ | ||
751 | 0x0c, /* 00001100 */ | ||
752 | 0x0c, /* 00001100 */ | ||
753 | 0x0c, /* 00001100 */ | ||
754 | 0xcc, /* 11001100 */ | ||
755 | 0xcc, /* 11001100 */ | ||
756 | 0x78, /* 01111000 */ | ||
757 | 0x00, /* 00000000 */ | ||
758 | |||
759 | /* 75 0x4b 'K' */ | ||
760 | 0xe6, /* 11100110 */ | ||
761 | 0x66, /* 01100110 */ | ||
762 | 0x6c, /* 01101100 */ | ||
763 | 0x78, /* 01111000 */ | ||
764 | 0x6c, /* 01101100 */ | ||
765 | 0x66, /* 01100110 */ | ||
766 | 0xe6, /* 11100110 */ | ||
767 | 0x00, /* 00000000 */ | ||
768 | |||
769 | /* 76 0x4c 'L' */ | ||
770 | 0xf0, /* 11110000 */ | ||
771 | 0x60, /* 01100000 */ | ||
772 | 0x60, /* 01100000 */ | ||
773 | 0x60, /* 01100000 */ | ||
774 | 0x62, /* 01100010 */ | ||
775 | 0x66, /* 01100110 */ | ||
776 | 0xfe, /* 11111110 */ | ||
777 | 0x00, /* 00000000 */ | ||
778 | |||
779 | /* 77 0x4d 'M' */ | ||
780 | 0xc6, /* 11000110 */ | ||
781 | 0xee, /* 11101110 */ | ||
782 | 0xfe, /* 11111110 */ | ||
783 | 0xfe, /* 11111110 */ | ||
784 | 0xd6, /* 11010110 */ | ||
785 | 0xc6, /* 11000110 */ | ||
786 | 0xc6, /* 11000110 */ | ||
787 | 0x00, /* 00000000 */ | ||
788 | |||
789 | /* 78 0x4e 'N' */ | ||
790 | 0xc6, /* 11000110 */ | ||
791 | 0xe6, /* 11100110 */ | ||
792 | 0xf6, /* 11110110 */ | ||
793 | 0xde, /* 11011110 */ | ||
794 | 0xce, /* 11001110 */ | ||
795 | 0xc6, /* 11000110 */ | ||
796 | 0xc6, /* 11000110 */ | ||
797 | 0x00, /* 00000000 */ | ||
798 | |||
799 | /* 79 0x4f 'O' */ | ||
800 | 0x7c, /* 01111100 */ | ||
801 | 0xc6, /* 11000110 */ | ||
802 | 0xc6, /* 11000110 */ | ||
803 | 0xc6, /* 11000110 */ | ||
804 | 0xc6, /* 11000110 */ | ||
805 | 0xc6, /* 11000110 */ | ||
806 | 0x7c, /* 01111100 */ | ||
807 | 0x00, /* 00000000 */ | ||
808 | |||
809 | /* 80 0x50 'P' */ | ||
810 | 0xfc, /* 11111100 */ | ||
811 | 0x66, /* 01100110 */ | ||
812 | 0x66, /* 01100110 */ | ||
813 | 0x7c, /* 01111100 */ | ||
814 | 0x60, /* 01100000 */ | ||
815 | 0x60, /* 01100000 */ | ||
816 | 0xf0, /* 11110000 */ | ||
817 | 0x00, /* 00000000 */ | ||
818 | |||
819 | /* 81 0x51 'Q' */ | ||
820 | 0x7c, /* 01111100 */ | ||
821 | 0xc6, /* 11000110 */ | ||
822 | 0xc6, /* 11000110 */ | ||
823 | 0xc6, /* 11000110 */ | ||
824 | 0xc6, /* 11000110 */ | ||
825 | 0xce, /* 11001110 */ | ||
826 | 0x7c, /* 01111100 */ | ||
827 | 0x0e, /* 00001110 */ | ||
828 | |||
829 | /* 82 0x52 'R' */ | ||
830 | 0xfc, /* 11111100 */ | ||
831 | 0x66, /* 01100110 */ | ||
832 | 0x66, /* 01100110 */ | ||
833 | 0x7c, /* 01111100 */ | ||
834 | 0x6c, /* 01101100 */ | ||
835 | 0x66, /* 01100110 */ | ||
836 | 0xe6, /* 11100110 */ | ||
837 | 0x00, /* 00000000 */ | ||
838 | |||
839 | /* 83 0x53 'S' */ | ||
840 | 0x3c, /* 00111100 */ | ||
841 | 0x66, /* 01100110 */ | ||
842 | 0x30, /* 00110000 */ | ||
843 | 0x18, /* 00011000 */ | ||
844 | 0x0c, /* 00001100 */ | ||
845 | 0x66, /* 01100110 */ | ||
846 | 0x3c, /* 00111100 */ | ||
847 | 0x00, /* 00000000 */ | ||
848 | |||
849 | /* 84 0x54 'T' */ | ||
850 | 0x7e, /* 01111110 */ | ||
851 | 0x7e, /* 01111110 */ | ||
852 | 0x5a, /* 01011010 */ | ||
853 | 0x18, /* 00011000 */ | ||
854 | 0x18, /* 00011000 */ | ||
855 | 0x18, /* 00011000 */ | ||
856 | 0x3c, /* 00111100 */ | ||
857 | 0x00, /* 00000000 */ | ||
858 | |||
859 | /* 85 0x55 'U' */ | ||
860 | 0xc6, /* 11000110 */ | ||
861 | 0xc6, /* 11000110 */ | ||
862 | 0xc6, /* 11000110 */ | ||
863 | 0xc6, /* 11000110 */ | ||
864 | 0xc6, /* 11000110 */ | ||
865 | 0xc6, /* 11000110 */ | ||
866 | 0x7c, /* 01111100 */ | ||
867 | 0x00, /* 00000000 */ | ||
868 | |||
869 | /* 86 0x56 'V' */ | ||
870 | 0xc6, /* 11000110 */ | ||
871 | 0xc6, /* 11000110 */ | ||
872 | 0xc6, /* 11000110 */ | ||
873 | 0xc6, /* 11000110 */ | ||
874 | 0xc6, /* 11000110 */ | ||
875 | 0x6c, /* 01101100 */ | ||
876 | 0x38, /* 00111000 */ | ||
877 | 0x00, /* 00000000 */ | ||
878 | |||
879 | /* 87 0x57 'W' */ | ||
880 | 0xc6, /* 11000110 */ | ||
881 | 0xc6, /* 11000110 */ | ||
882 | 0xc6, /* 11000110 */ | ||
883 | 0xd6, /* 11010110 */ | ||
884 | 0xd6, /* 11010110 */ | ||
885 | 0xfe, /* 11111110 */ | ||
886 | 0x6c, /* 01101100 */ | ||
887 | 0x00, /* 00000000 */ | ||
888 | |||
889 | /* 88 0x58 'X' */ | ||
890 | 0xc6, /* 11000110 */ | ||
891 | 0xc6, /* 11000110 */ | ||
892 | 0x6c, /* 01101100 */ | ||
893 | 0x38, /* 00111000 */ | ||
894 | 0x6c, /* 01101100 */ | ||
895 | 0xc6, /* 11000110 */ | ||
896 | 0xc6, /* 11000110 */ | ||
897 | 0x00, /* 00000000 */ | ||
898 | |||
899 | /* 89 0x59 'Y' */ | ||
900 | 0x66, /* 01100110 */ | ||
901 | 0x66, /* 01100110 */ | ||
902 | 0x66, /* 01100110 */ | ||
903 | 0x3c, /* 00111100 */ | ||
904 | 0x18, /* 00011000 */ | ||
905 | 0x18, /* 00011000 */ | ||
906 | 0x3c, /* 00111100 */ | ||
907 | 0x00, /* 00000000 */ | ||
908 | |||
909 | /* 90 0x5a 'Z' */ | ||
910 | 0xfe, /* 11111110 */ | ||
911 | 0xc6, /* 11000110 */ | ||
912 | 0x8c, /* 10001100 */ | ||
913 | 0x18, /* 00011000 */ | ||
914 | 0x32, /* 00110010 */ | ||
915 | 0x66, /* 01100110 */ | ||
916 | 0xfe, /* 11111110 */ | ||
917 | 0x00, /* 00000000 */ | ||
918 | |||
919 | /* 91 0x5b '[' */ | ||
920 | 0x3c, /* 00111100 */ | ||
921 | 0x30, /* 00110000 */ | ||
922 | 0x30, /* 00110000 */ | ||
923 | 0x30, /* 00110000 */ | ||
924 | 0x30, /* 00110000 */ | ||
925 | 0x30, /* 00110000 */ | ||
926 | 0x3c, /* 00111100 */ | ||
927 | 0x00, /* 00000000 */ | ||
928 | |||
929 | /* 92 0x5c '\' */ | ||
930 | 0xc0, /* 11000000 */ | ||
931 | 0x60, /* 01100000 */ | ||
932 | 0x30, /* 00110000 */ | ||
933 | 0x18, /* 00011000 */ | ||
934 | 0x0c, /* 00001100 */ | ||
935 | 0x06, /* 00000110 */ | ||
936 | 0x02, /* 00000010 */ | ||
937 | 0x00, /* 00000000 */ | ||
938 | |||
939 | /* 93 0x5d ']' */ | ||
940 | 0x3c, /* 00111100 */ | ||
941 | 0x0c, /* 00001100 */ | ||
942 | 0x0c, /* 00001100 */ | ||
943 | 0x0c, /* 00001100 */ | ||
944 | 0x0c, /* 00001100 */ | ||
945 | 0x0c, /* 00001100 */ | ||
946 | 0x3c, /* 00111100 */ | ||
947 | 0x00, /* 00000000 */ | ||
948 | |||
949 | /* 94 0x5e '^' */ | ||
950 | 0x10, /* 00010000 */ | ||
951 | 0x38, /* 00111000 */ | ||
952 | 0x6c, /* 01101100 */ | ||
953 | 0xc6, /* 11000110 */ | ||
954 | 0x00, /* 00000000 */ | ||
955 | 0x00, /* 00000000 */ | ||
956 | 0x00, /* 00000000 */ | ||
957 | 0x00, /* 00000000 */ | ||
958 | |||
959 | /* 95 0x5f '_' */ | ||
960 | 0x00, /* 00000000 */ | ||
961 | 0x00, /* 00000000 */ | ||
962 | 0x00, /* 00000000 */ | ||
963 | 0x00, /* 00000000 */ | ||
964 | 0x00, /* 00000000 */ | ||
965 | 0x00, /* 00000000 */ | ||
966 | 0x00, /* 00000000 */ | ||
967 | 0xff, /* 11111111 */ | ||
968 | |||
969 | /* 96 0x60 '`' */ | ||
970 | 0x30, /* 00110000 */ | ||
971 | 0x18, /* 00011000 */ | ||
972 | 0x0c, /* 00001100 */ | ||
973 | 0x00, /* 00000000 */ | ||
974 | 0x00, /* 00000000 */ | ||
975 | 0x00, /* 00000000 */ | ||
976 | 0x00, /* 00000000 */ | ||
977 | 0x00, /* 00000000 */ | ||
978 | |||
979 | /* 97 0x61 'a' */ | ||
980 | 0x00, /* 00000000 */ | ||
981 | 0x00, /* 00000000 */ | ||
982 | 0x78, /* 01111000 */ | ||
983 | 0x0c, /* 00001100 */ | ||
984 | 0x7c, /* 01111100 */ | ||
985 | 0xcc, /* 11001100 */ | ||
986 | 0x76, /* 01110110 */ | ||
987 | 0x00, /* 00000000 */ | ||
988 | |||
989 | /* 98 0x62 'b' */ | ||
990 | 0xe0, /* 11100000 */ | ||
991 | 0x60, /* 01100000 */ | ||
992 | 0x7c, /* 01111100 */ | ||
993 | 0x66, /* 01100110 */ | ||
994 | 0x66, /* 01100110 */ | ||
995 | 0x66, /* 01100110 */ | ||
996 | 0xdc, /* 11011100 */ | ||
997 | 0x00, /* 00000000 */ | ||
998 | |||
999 | /* 99 0x63 'c' */ | ||
1000 | 0x00, /* 00000000 */ | ||
1001 | 0x00, /* 00000000 */ | ||
1002 | 0x7c, /* 01111100 */ | ||
1003 | 0xc6, /* 11000110 */ | ||
1004 | 0xc0, /* 11000000 */ | ||
1005 | 0xc6, /* 11000110 */ | ||
1006 | 0x7c, /* 01111100 */ | ||
1007 | 0x00, /* 00000000 */ | ||
1008 | |||
1009 | /* 100 0x64 'd' */ | ||
1010 | 0x1c, /* 00011100 */ | ||
1011 | 0x0c, /* 00001100 */ | ||
1012 | 0x7c, /* 01111100 */ | ||
1013 | 0xcc, /* 11001100 */ | ||
1014 | 0xcc, /* 11001100 */ | ||
1015 | 0xcc, /* 11001100 */ | ||
1016 | 0x76, /* 01110110 */ | ||
1017 | 0x00, /* 00000000 */ | ||
1018 | |||
1019 | /* 101 0x65 'e' */ | ||
1020 | 0x00, /* 00000000 */ | ||
1021 | 0x00, /* 00000000 */ | ||
1022 | 0x7c, /* 01111100 */ | ||
1023 | 0xc6, /* 11000110 */ | ||
1024 | 0xfe, /* 11111110 */ | ||
1025 | 0xc0, /* 11000000 */ | ||
1026 | 0x7c, /* 01111100 */ | ||
1027 | 0x00, /* 00000000 */ | ||
1028 | |||
1029 | /* 102 0x66 'f' */ | ||
1030 | 0x3c, /* 00111100 */ | ||
1031 | 0x66, /* 01100110 */ | ||
1032 | 0x60, /* 01100000 */ | ||
1033 | 0xf8, /* 11111000 */ | ||
1034 | 0x60, /* 01100000 */ | ||
1035 | 0x60, /* 01100000 */ | ||
1036 | 0xf0, /* 11110000 */ | ||
1037 | 0x00, /* 00000000 */ | ||
1038 | |||
1039 | /* 103 0x67 'g' */ | ||
1040 | 0x00, /* 00000000 */ | ||
1041 | 0x00, /* 00000000 */ | ||
1042 | 0x76, /* 01110110 */ | ||
1043 | 0xcc, /* 11001100 */ | ||
1044 | 0xcc, /* 11001100 */ | ||
1045 | 0x7c, /* 01111100 */ | ||
1046 | 0x0c, /* 00001100 */ | ||
1047 | 0xf8, /* 11111000 */ | ||
1048 | |||
1049 | /* 104 0x68 'h' */ | ||
1050 | 0xe0, /* 11100000 */ | ||
1051 | 0x60, /* 01100000 */ | ||
1052 | 0x6c, /* 01101100 */ | ||
1053 | 0x76, /* 01110110 */ | ||
1054 | 0x66, /* 01100110 */ | ||
1055 | 0x66, /* 01100110 */ | ||
1056 | 0xe6, /* 11100110 */ | ||
1057 | 0x00, /* 00000000 */ | ||
1058 | |||
1059 | /* 105 0x69 'i' */ | ||
1060 | 0x18, /* 00011000 */ | ||
1061 | 0x00, /* 00000000 */ | ||
1062 | 0x38, /* 00111000 */ | ||
1063 | 0x18, /* 00011000 */ | ||
1064 | 0x18, /* 00011000 */ | ||
1065 | 0x18, /* 00011000 */ | ||
1066 | 0x3c, /* 00111100 */ | ||
1067 | 0x00, /* 00000000 */ | ||
1068 | |||
1069 | /* 106 0x6a 'j' */ | ||
1070 | 0x06, /* 00000110 */ | ||
1071 | 0x00, /* 00000000 */ | ||
1072 | 0x06, /* 00000110 */ | ||
1073 | 0x06, /* 00000110 */ | ||
1074 | 0x06, /* 00000110 */ | ||
1075 | 0x66, /* 01100110 */ | ||
1076 | 0x66, /* 01100110 */ | ||
1077 | 0x3c, /* 00111100 */ | ||
1078 | |||
1079 | /* 107 0x6b 'k' */ | ||
1080 | 0xe0, /* 11100000 */ | ||
1081 | 0x60, /* 01100000 */ | ||
1082 | 0x66, /* 01100110 */ | ||
1083 | 0x6c, /* 01101100 */ | ||
1084 | 0x78, /* 01111000 */ | ||
1085 | 0x6c, /* 01101100 */ | ||
1086 | 0xe6, /* 11100110 */ | ||
1087 | 0x00, /* 00000000 */ | ||
1088 | |||
1089 | /* 108 0x6c 'l' */ | ||
1090 | 0x38, /* 00111000 */ | ||
1091 | 0x18, /* 00011000 */ | ||
1092 | 0x18, /* 00011000 */ | ||
1093 | 0x18, /* 00011000 */ | ||
1094 | 0x18, /* 00011000 */ | ||
1095 | 0x18, /* 00011000 */ | ||
1096 | 0x3c, /* 00111100 */ | ||
1097 | 0x00, /* 00000000 */ | ||
1098 | |||
1099 | /* 109 0x6d 'm' */ | ||
1100 | 0x00, /* 00000000 */ | ||
1101 | 0x00, /* 00000000 */ | ||
1102 | 0xec, /* 11101100 */ | ||
1103 | 0xfe, /* 11111110 */ | ||
1104 | 0xd6, /* 11010110 */ | ||
1105 | 0xd6, /* 11010110 */ | ||
1106 | 0xd6, /* 11010110 */ | ||
1107 | 0x00, /* 00000000 */ | ||
1108 | |||
1109 | /* 110 0x6e 'n' */ | ||
1110 | 0x00, /* 00000000 */ | ||
1111 | 0x00, /* 00000000 */ | ||
1112 | 0xdc, /* 11011100 */ | ||
1113 | 0x66, /* 01100110 */ | ||
1114 | 0x66, /* 01100110 */ | ||
1115 | 0x66, /* 01100110 */ | ||
1116 | 0x66, /* 01100110 */ | ||
1117 | 0x00, /* 00000000 */ | ||
1118 | |||
1119 | /* 111 0x6f 'o' */ | ||
1120 | 0x00, /* 00000000 */ | ||
1121 | 0x00, /* 00000000 */ | ||
1122 | 0x7c, /* 01111100 */ | ||
1123 | 0xc6, /* 11000110 */ | ||
1124 | 0xc6, /* 11000110 */ | ||
1125 | 0xc6, /* 11000110 */ | ||
1126 | 0x7c, /* 01111100 */ | ||
1127 | 0x00, /* 00000000 */ | ||
1128 | |||
1129 | /* 112 0x70 'p' */ | ||
1130 | 0x00, /* 00000000 */ | ||
1131 | 0x00, /* 00000000 */ | ||
1132 | 0xdc, /* 11011100 */ | ||
1133 | 0x66, /* 01100110 */ | ||
1134 | 0x66, /* 01100110 */ | ||
1135 | 0x7c, /* 01111100 */ | ||
1136 | 0x60, /* 01100000 */ | ||
1137 | 0xf0, /* 11110000 */ | ||
1138 | |||
1139 | /* 113 0x71 'q' */ | ||
1140 | 0x00, /* 00000000 */ | ||
1141 | 0x00, /* 00000000 */ | ||
1142 | 0x76, /* 01110110 */ | ||
1143 | 0xcc, /* 11001100 */ | ||
1144 | 0xcc, /* 11001100 */ | ||
1145 | 0x7c, /* 01111100 */ | ||
1146 | 0x0c, /* 00001100 */ | ||
1147 | 0x1e, /* 00011110 */ | ||
1148 | |||
1149 | /* 114 0x72 'r' */ | ||
1150 | 0x00, /* 00000000 */ | ||
1151 | 0x00, /* 00000000 */ | ||
1152 | 0xdc, /* 11011100 */ | ||
1153 | 0x76, /* 01110110 */ | ||
1154 | 0x60, /* 01100000 */ | ||
1155 | 0x60, /* 01100000 */ | ||
1156 | 0xf0, /* 11110000 */ | ||
1157 | 0x00, /* 00000000 */ | ||
1158 | |||
1159 | /* 115 0x73 's' */ | ||
1160 | 0x00, /* 00000000 */ | ||
1161 | 0x00, /* 00000000 */ | ||
1162 | 0x7e, /* 01111110 */ | ||
1163 | 0xc0, /* 11000000 */ | ||
1164 | 0x7c, /* 01111100 */ | ||
1165 | 0x06, /* 00000110 */ | ||
1166 | 0xfc, /* 11111100 */ | ||
1167 | 0x00, /* 00000000 */ | ||
1168 | |||
1169 | /* 116 0x74 't' */ | ||
1170 | 0x30, /* 00110000 */ | ||
1171 | 0x30, /* 00110000 */ | ||
1172 | 0xfc, /* 11111100 */ | ||
1173 | 0x30, /* 00110000 */ | ||
1174 | 0x30, /* 00110000 */ | ||
1175 | 0x36, /* 00110110 */ | ||
1176 | 0x1c, /* 00011100 */ | ||
1177 | 0x00, /* 00000000 */ | ||
1178 | |||
1179 | /* 117 0x75 'u' */ | ||
1180 | 0x00, /* 00000000 */ | ||
1181 | 0x00, /* 00000000 */ | ||
1182 | 0xcc, /* 11001100 */ | ||
1183 | 0xcc, /* 11001100 */ | ||
1184 | 0xcc, /* 11001100 */ | ||
1185 | 0xcc, /* 11001100 */ | ||
1186 | 0x76, /* 01110110 */ | ||
1187 | 0x00, /* 00000000 */ | ||
1188 | |||
1189 | /* 118 0x76 'v' */ | ||
1190 | 0x00, /* 00000000 */ | ||
1191 | 0x00, /* 00000000 */ | ||
1192 | 0xc6, /* 11000110 */ | ||
1193 | 0xc6, /* 11000110 */ | ||
1194 | 0xc6, /* 11000110 */ | ||
1195 | 0x6c, /* 01101100 */ | ||
1196 | 0x38, /* 00111000 */ | ||
1197 | 0x00, /* 00000000 */ | ||
1198 | |||
1199 | /* 119 0x77 'w' */ | ||
1200 | 0x00, /* 00000000 */ | ||
1201 | 0x00, /* 00000000 */ | ||
1202 | 0xc6, /* 11000110 */ | ||
1203 | 0xd6, /* 11010110 */ | ||
1204 | 0xd6, /* 11010110 */ | ||
1205 | 0xfe, /* 11111110 */ | ||
1206 | 0x6c, /* 01101100 */ | ||
1207 | 0x00, /* 00000000 */ | ||
1208 | |||
1209 | /* 120 0x78 'x' */ | ||
1210 | 0x00, /* 00000000 */ | ||
1211 | 0x00, /* 00000000 */ | ||
1212 | 0xc6, /* 11000110 */ | ||
1213 | 0x6c, /* 01101100 */ | ||
1214 | 0x38, /* 00111000 */ | ||
1215 | 0x6c, /* 01101100 */ | ||
1216 | 0xc6, /* 11000110 */ | ||
1217 | 0x00, /* 00000000 */ | ||
1218 | |||
1219 | /* 121 0x79 'y' */ | ||
1220 | 0x00, /* 00000000 */ | ||
1221 | 0x00, /* 00000000 */ | ||
1222 | 0xc6, /* 11000110 */ | ||
1223 | 0xc6, /* 11000110 */ | ||
1224 | 0xc6, /* 11000110 */ | ||
1225 | 0x7e, /* 01111110 */ | ||
1226 | 0x06, /* 00000110 */ | ||
1227 | 0xfc, /* 11111100 */ | ||
1228 | |||
1229 | /* 122 0x7a 'z' */ | ||
1230 | 0x00, /* 00000000 */ | ||
1231 | 0x00, /* 00000000 */ | ||
1232 | 0x7e, /* 01111110 */ | ||
1233 | 0x4c, /* 01001100 */ | ||
1234 | 0x18, /* 00011000 */ | ||
1235 | 0x32, /* 00110010 */ | ||
1236 | 0x7e, /* 01111110 */ | ||
1237 | 0x00, /* 00000000 */ | ||
1238 | |||
1239 | /* 123 0x7b '{' */ | ||
1240 | 0x0e, /* 00001110 */ | ||
1241 | 0x18, /* 00011000 */ | ||
1242 | 0x18, /* 00011000 */ | ||
1243 | 0x70, /* 01110000 */ | ||
1244 | 0x18, /* 00011000 */ | ||
1245 | 0x18, /* 00011000 */ | ||
1246 | 0x0e, /* 00001110 */ | ||
1247 | 0x00, /* 00000000 */ | ||
1248 | |||
1249 | /* 124 0x7c '|' */ | ||
1250 | 0x18, /* 00011000 */ | ||
1251 | 0x18, /* 00011000 */ | ||
1252 | 0x18, /* 00011000 */ | ||
1253 | 0x18, /* 00011000 */ | ||
1254 | 0x18, /* 00011000 */ | ||
1255 | 0x18, /* 00011000 */ | ||
1256 | 0x18, /* 00011000 */ | ||
1257 | 0x00, /* 00000000 */ | ||
1258 | |||
1259 | /* 125 0x7d '}' */ | ||
1260 | 0x70, /* 01110000 */ | ||
1261 | 0x18, /* 00011000 */ | ||
1262 | 0x18, /* 00011000 */ | ||
1263 | 0x0e, /* 00001110 */ | ||
1264 | 0x18, /* 00011000 */ | ||
1265 | 0x18, /* 00011000 */ | ||
1266 | 0x70, /* 01110000 */ | ||
1267 | 0x00, /* 00000000 */ | ||
1268 | |||
1269 | /* 126 0x7e '~' */ | ||
1270 | 0x76, /* 01110110 */ | ||
1271 | 0xdc, /* 11011100 */ | ||
1272 | 0x00, /* 00000000 */ | ||
1273 | 0x00, /* 00000000 */ | ||
1274 | 0x00, /* 00000000 */ | ||
1275 | 0x00, /* 00000000 */ | ||
1276 | 0x00, /* 00000000 */ | ||
1277 | 0x00, /* 00000000 */ | ||
1278 | |||
1279 | /* 127 0x7f '' */ | ||
1280 | 0x00, /* 00000000 */ | ||
1281 | 0x10, /* 00010000 */ | ||
1282 | 0x38, /* 00111000 */ | ||
1283 | 0x6c, /* 01101100 */ | ||
1284 | 0xc6, /* 11000110 */ | ||
1285 | 0xc6, /* 11000110 */ | ||
1286 | 0xfe, /* 11111110 */ | ||
1287 | 0x00, /* 00000000 */ | ||
1288 | |||
1289 | /* 128 0x80 '€' */ | ||
1290 | 0x7c, /* 01111100 */ | ||
1291 | 0xc6, /* 11000110 */ | ||
1292 | 0xc0, /* 11000000 */ | ||
1293 | 0xc0, /* 11000000 */ | ||
1294 | 0xc6, /* 11000110 */ | ||
1295 | 0x7c, /* 01111100 */ | ||
1296 | 0x0c, /* 00001100 */ | ||
1297 | 0x78, /* 01111000 */ | ||
1298 | |||
1299 | /* 129 0x81 '' */ | ||
1300 | 0xcc, /* 11001100 */ | ||
1301 | 0x00, /* 00000000 */ | ||
1302 | 0xcc, /* 11001100 */ | ||
1303 | 0xcc, /* 11001100 */ | ||
1304 | 0xcc, /* 11001100 */ | ||
1305 | 0xcc, /* 11001100 */ | ||
1306 | 0x76, /* 01110110 */ | ||
1307 | 0x00, /* 00000000 */ | ||
1308 | |||
1309 | /* 130 0x82 '‚' */ | ||
1310 | 0x0c, /* 00001100 */ | ||
1311 | 0x18, /* 00011000 */ | ||
1312 | 0x7c, /* 01111100 */ | ||
1313 | 0xc6, /* 11000110 */ | ||
1314 | 0xfe, /* 11111110 */ | ||
1315 | 0xc0, /* 11000000 */ | ||
1316 | 0x7c, /* 01111100 */ | ||
1317 | 0x00, /* 00000000 */ | ||
1318 | |||
1319 | /* 131 0x83 'ƒ' */ | ||
1320 | 0x7c, /* 01111100 */ | ||
1321 | 0x82, /* 10000010 */ | ||
1322 | 0x78, /* 01111000 */ | ||
1323 | 0x0c, /* 00001100 */ | ||
1324 | 0x7c, /* 01111100 */ | ||
1325 | 0xcc, /* 11001100 */ | ||
1326 | 0x76, /* 01110110 */ | ||
1327 | 0x00, /* 00000000 */ | ||
1328 | |||
1329 | /* 132 0x84 '„' */ | ||
1330 | 0xc6, /* 11000110 */ | ||
1331 | 0x00, /* 00000000 */ | ||
1332 | 0x78, /* 01111000 */ | ||
1333 | 0x0c, /* 00001100 */ | ||
1334 | 0x7c, /* 01111100 */ | ||
1335 | 0xcc, /* 11001100 */ | ||
1336 | 0x76, /* 01110110 */ | ||
1337 | 0x00, /* 00000000 */ | ||
1338 | |||
1339 | /* 133 0x85 '…' */ | ||
1340 | 0x30, /* 00110000 */ | ||
1341 | 0x18, /* 00011000 */ | ||
1342 | 0x78, /* 01111000 */ | ||
1343 | 0x0c, /* 00001100 */ | ||
1344 | 0x7c, /* 01111100 */ | ||
1345 | 0xcc, /* 11001100 */ | ||
1346 | 0x76, /* 01110110 */ | ||
1347 | 0x00, /* 00000000 */ | ||
1348 | |||
1349 | /* 134 0x86 '†' */ | ||
1350 | 0x30, /* 00110000 */ | ||
1351 | 0x30, /* 00110000 */ | ||
1352 | 0x78, /* 01111000 */ | ||
1353 | 0x0c, /* 00001100 */ | ||
1354 | 0x7c, /* 01111100 */ | ||
1355 | 0xcc, /* 11001100 */ | ||
1356 | 0x76, /* 01110110 */ | ||
1357 | 0x00, /* 00000000 */ | ||
1358 | |||
1359 | /* 135 0x87 '‡' */ | ||
1360 | 0x00, /* 00000000 */ | ||
1361 | 0x00, /* 00000000 */ | ||
1362 | 0x7e, /* 01111110 */ | ||
1363 | 0xc0, /* 11000000 */ | ||
1364 | 0xc0, /* 11000000 */ | ||
1365 | 0x7e, /* 01111110 */ | ||
1366 | 0x0c, /* 00001100 */ | ||
1367 | 0x38, /* 00111000 */ | ||
1368 | |||
1369 | /* 136 0x88 'ˆ' */ | ||
1370 | 0x7c, /* 01111100 */ | ||
1371 | 0x82, /* 10000010 */ | ||
1372 | 0x7c, /* 01111100 */ | ||
1373 | 0xc6, /* 11000110 */ | ||
1374 | 0xfe, /* 11111110 */ | ||
1375 | 0xc0, /* 11000000 */ | ||
1376 | 0x7c, /* 01111100 */ | ||
1377 | 0x00, /* 00000000 */ | ||
1378 | |||
1379 | /* 137 0x89 '‰' */ | ||
1380 | 0xc6, /* 11000110 */ | ||
1381 | 0x00, /* 00000000 */ | ||
1382 | 0x7c, /* 01111100 */ | ||
1383 | 0xc6, /* 11000110 */ | ||
1384 | 0xfe, /* 11111110 */ | ||
1385 | 0xc0, /* 11000000 */ | ||
1386 | 0x7c, /* 01111100 */ | ||
1387 | 0x00, /* 00000000 */ | ||
1388 | |||
1389 | /* 138 0x8a 'Š' */ | ||
1390 | 0x30, /* 00110000 */ | ||
1391 | 0x18, /* 00011000 */ | ||
1392 | 0x7c, /* 01111100 */ | ||
1393 | 0xc6, /* 11000110 */ | ||
1394 | 0xfe, /* 11111110 */ | ||
1395 | 0xc0, /* 11000000 */ | ||
1396 | 0x7c, /* 01111100 */ | ||
1397 | 0x00, /* 00000000 */ | ||
1398 | |||
1399 | /* 139 0x8b '‹' */ | ||
1400 | 0x66, /* 01100110 */ | ||
1401 | 0x00, /* 00000000 */ | ||
1402 | 0x38, /* 00111000 */ | ||
1403 | 0x18, /* 00011000 */ | ||
1404 | 0x18, /* 00011000 */ | ||
1405 | 0x18, /* 00011000 */ | ||
1406 | 0x3c, /* 00111100 */ | ||
1407 | 0x00, /* 00000000 */ | ||
1408 | |||
1409 | /* 140 0x8c 'Œ' */ | ||
1410 | 0x7c, /* 01111100 */ | ||
1411 | 0x82, /* 10000010 */ | ||
1412 | 0x38, /* 00111000 */ | ||
1413 | 0x18, /* 00011000 */ | ||
1414 | 0x18, /* 00011000 */ | ||
1415 | 0x18, /* 00011000 */ | ||
1416 | 0x3c, /* 00111100 */ | ||
1417 | 0x00, /* 00000000 */ | ||
1418 | |||
1419 | /* 141 0x8d '' */ | ||
1420 | 0x30, /* 00110000 */ | ||
1421 | 0x18, /* 00011000 */ | ||
1422 | 0x00, /* 00000000 */ | ||
1423 | 0x38, /* 00111000 */ | ||
1424 | 0x18, /* 00011000 */ | ||
1425 | 0x18, /* 00011000 */ | ||
1426 | 0x3c, /* 00111100 */ | ||
1427 | 0x00, /* 00000000 */ | ||
1428 | |||
1429 | /* 142 0x8e 'Ž' */ | ||
1430 | 0xc6, /* 11000110 */ | ||
1431 | 0x38, /* 00111000 */ | ||
1432 | 0x6c, /* 01101100 */ | ||
1433 | 0xc6, /* 11000110 */ | ||
1434 | 0xfe, /* 11111110 */ | ||
1435 | 0xc6, /* 11000110 */ | ||
1436 | 0xc6, /* 11000110 */ | ||
1437 | 0x00, /* 00000000 */ | ||
1438 | |||
1439 | /* 143 0x8f '' */ | ||
1440 | 0x38, /* 00111000 */ | ||
1441 | 0x6c, /* 01101100 */ | ||
1442 | 0x7c, /* 01111100 */ | ||
1443 | 0xc6, /* 11000110 */ | ||
1444 | 0xfe, /* 11111110 */ | ||
1445 | 0xc6, /* 11000110 */ | ||
1446 | 0xc6, /* 11000110 */ | ||
1447 | 0x00, /* 00000000 */ | ||
1448 | |||
1449 | /* 144 0x90 '' */ | ||
1450 | 0x18, /* 00011000 */ | ||
1451 | 0x30, /* 00110000 */ | ||
1452 | 0xfe, /* 11111110 */ | ||
1453 | 0xc0, /* 11000000 */ | ||
1454 | 0xf8, /* 11111000 */ | ||
1455 | 0xc0, /* 11000000 */ | ||
1456 | 0xfe, /* 11111110 */ | ||
1457 | 0x00, /* 00000000 */ | ||
1458 | |||
1459 | /* 145 0x91 '‘' */ | ||
1460 | 0x00, /* 00000000 */ | ||
1461 | 0x00, /* 00000000 */ | ||
1462 | 0x7e, /* 01111110 */ | ||
1463 | 0x18, /* 00011000 */ | ||
1464 | 0x7e, /* 01111110 */ | ||
1465 | 0xd8, /* 11011000 */ | ||
1466 | 0x7e, /* 01111110 */ | ||
1467 | 0x00, /* 00000000 */ | ||
1468 | |||
1469 | /* 146 0x92 '’' */ | ||
1470 | 0x3e, /* 00111110 */ | ||
1471 | 0x6c, /* 01101100 */ | ||
1472 | 0xcc, /* 11001100 */ | ||
1473 | 0xfe, /* 11111110 */ | ||
1474 | 0xcc, /* 11001100 */ | ||
1475 | 0xcc, /* 11001100 */ | ||
1476 | 0xce, /* 11001110 */ | ||
1477 | 0x00, /* 00000000 */ | ||
1478 | |||
1479 | /* 147 0x93 '“' */ | ||
1480 | 0x7c, /* 01111100 */ | ||
1481 | 0x82, /* 10000010 */ | ||
1482 | 0x7c, /* 01111100 */ | ||
1483 | 0xc6, /* 11000110 */ | ||
1484 | 0xc6, /* 11000110 */ | ||
1485 | 0xc6, /* 11000110 */ | ||
1486 | 0x7c, /* 01111100 */ | ||
1487 | 0x00, /* 00000000 */ | ||
1488 | |||
1489 | /* 148 0x94 '”' */ | ||
1490 | 0xc6, /* 11000110 */ | ||
1491 | 0x00, /* 00000000 */ | ||
1492 | 0x7c, /* 01111100 */ | ||
1493 | 0xc6, /* 11000110 */ | ||
1494 | 0xc6, /* 11000110 */ | ||
1495 | 0xc6, /* 11000110 */ | ||
1496 | 0x7c, /* 01111100 */ | ||
1497 | 0x00, /* 00000000 */ | ||
1498 | |||
1499 | /* 149 0x95 '•' */ | ||
1500 | 0x30, /* 00110000 */ | ||
1501 | 0x18, /* 00011000 */ | ||
1502 | 0x7c, /* 01111100 */ | ||
1503 | 0xc6, /* 11000110 */ | ||
1504 | 0xc6, /* 11000110 */ | ||
1505 | 0xc6, /* 11000110 */ | ||
1506 | 0x7c, /* 01111100 */ | ||
1507 | 0x00, /* 00000000 */ | ||
1508 | |||
1509 | /* 150 0x96 '–' */ | ||
1510 | 0x78, /* 01111000 */ | ||
1511 | 0x84, /* 10000100 */ | ||
1512 | 0x00, /* 00000000 */ | ||
1513 | 0xcc, /* 11001100 */ | ||
1514 | 0xcc, /* 11001100 */ | ||
1515 | 0xcc, /* 11001100 */ | ||
1516 | 0x76, /* 01110110 */ | ||
1517 | 0x00, /* 00000000 */ | ||
1518 | |||
1519 | /* 151 0x97 '—' */ | ||
1520 | 0x60, /* 01100000 */ | ||
1521 | 0x30, /* 00110000 */ | ||
1522 | 0xcc, /* 11001100 */ | ||
1523 | 0xcc, /* 11001100 */ | ||
1524 | 0xcc, /* 11001100 */ | ||
1525 | 0xcc, /* 11001100 */ | ||
1526 | 0x76, /* 01110110 */ | ||
1527 | 0x00, /* 00000000 */ | ||
1528 | |||
1529 | /* 152 0x98 '˜' */ | ||
1530 | 0xc6, /* 11000110 */ | ||
1531 | 0x00, /* 00000000 */ | ||
1532 | 0xc6, /* 11000110 */ | ||
1533 | 0xc6, /* 11000110 */ | ||
1534 | 0xc6, /* 11000110 */ | ||
1535 | 0x7e, /* 01111110 */ | ||
1536 | 0x06, /* 00000110 */ | ||
1537 | 0xfc, /* 11111100 */ | ||
1538 | |||
1539 | /* 153 0x99 '™' */ | ||
1540 | 0xc6, /* 11000110 */ | ||
1541 | 0x38, /* 00111000 */ | ||
1542 | 0x6c, /* 01101100 */ | ||
1543 | 0xc6, /* 11000110 */ | ||
1544 | 0xc6, /* 11000110 */ | ||
1545 | 0x6c, /* 01101100 */ | ||
1546 | 0x38, /* 00111000 */ | ||
1547 | 0x00, /* 00000000 */ | ||
1548 | |||
1549 | /* 154 0x9a 'š' */ | ||
1550 | 0xc6, /* 11000110 */ | ||
1551 | 0x00, /* 00000000 */ | ||
1552 | 0xc6, /* 11000110 */ | ||
1553 | 0xc6, /* 11000110 */ | ||
1554 | 0xc6, /* 11000110 */ | ||
1555 | 0xc6, /* 11000110 */ | ||
1556 | 0x7c, /* 01111100 */ | ||
1557 | 0x00, /* 00000000 */ | ||
1558 | |||
1559 | /* 155 0x9b '›' */ | ||
1560 | 0x18, /* 00011000 */ | ||
1561 | 0x18, /* 00011000 */ | ||
1562 | 0x7e, /* 01111110 */ | ||
1563 | 0xc0, /* 11000000 */ | ||
1564 | 0xc0, /* 11000000 */ | ||
1565 | 0x7e, /* 01111110 */ | ||
1566 | 0x18, /* 00011000 */ | ||
1567 | 0x18, /* 00011000 */ | ||
1568 | |||
1569 | /* 156 0x9c 'œ' */ | ||
1570 | 0x38, /* 00111000 */ | ||
1571 | 0x6c, /* 01101100 */ | ||
1572 | 0x64, /* 01100100 */ | ||
1573 | 0xf0, /* 11110000 */ | ||
1574 | 0x60, /* 01100000 */ | ||
1575 | 0x66, /* 01100110 */ | ||
1576 | 0xfc, /* 11111100 */ | ||
1577 | 0x00, /* 00000000 */ | ||
1578 | |||
1579 | /* 157 0x9d '' */ | ||
1580 | 0x66, /* 01100110 */ | ||
1581 | 0x66, /* 01100110 */ | ||
1582 | 0x3c, /* 00111100 */ | ||
1583 | 0x7e, /* 01111110 */ | ||
1584 | 0x18, /* 00011000 */ | ||
1585 | 0x7e, /* 01111110 */ | ||
1586 | 0x18, /* 00011000 */ | ||
1587 | 0x18, /* 00011000 */ | ||
1588 | |||
1589 | /* 158 0x9e 'ž' */ | ||
1590 | 0xf8, /* 11111000 */ | ||
1591 | 0xcc, /* 11001100 */ | ||
1592 | 0xcc, /* 11001100 */ | ||
1593 | 0xfa, /* 11111010 */ | ||
1594 | 0xc6, /* 11000110 */ | ||
1595 | 0xcf, /* 11001111 */ | ||
1596 | 0xc6, /* 11000110 */ | ||
1597 | 0xc7, /* 11000111 */ | ||
1598 | |||
1599 | /* 159 0x9f 'Ÿ' */ | ||
1600 | 0x0e, /* 00001110 */ | ||
1601 | 0x1b, /* 00011011 */ | ||
1602 | 0x18, /* 00011000 */ | ||
1603 | 0x3c, /* 00111100 */ | ||
1604 | 0x18, /* 00011000 */ | ||
1605 | 0xd8, /* 11011000 */ | ||
1606 | 0x70, /* 01110000 */ | ||
1607 | 0x00, /* 00000000 */ | ||
1608 | |||
1609 | /* 160 0xa0 ' ' */ | ||
1610 | 0x18, /* 00011000 */ | ||
1611 | 0x30, /* 00110000 */ | ||
1612 | 0x78, /* 01111000 */ | ||
1613 | 0x0c, /* 00001100 */ | ||
1614 | 0x7c, /* 01111100 */ | ||
1615 | 0xcc, /* 11001100 */ | ||
1616 | 0x76, /* 01110110 */ | ||
1617 | 0x00, /* 00000000 */ | ||
1618 | |||
1619 | /* 161 0xa1 '¡' */ | ||
1620 | 0x0c, /* 00001100 */ | ||
1621 | 0x18, /* 00011000 */ | ||
1622 | 0x00, /* 00000000 */ | ||
1623 | 0x38, /* 00111000 */ | ||
1624 | 0x18, /* 00011000 */ | ||
1625 | 0x18, /* 00011000 */ | ||
1626 | 0x3c, /* 00111100 */ | ||
1627 | 0x00, /* 00000000 */ | ||
1628 | |||
1629 | /* 162 0xa2 '¢' */ | ||
1630 | 0x0c, /* 00001100 */ | ||
1631 | 0x18, /* 00011000 */ | ||
1632 | 0x7c, /* 01111100 */ | ||
1633 | 0xc6, /* 11000110 */ | ||
1634 | 0xc6, /* 11000110 */ | ||
1635 | 0xc6, /* 11000110 */ | ||
1636 | 0x7c, /* 01111100 */ | ||
1637 | 0x00, /* 00000000 */ | ||
1638 | |||
1639 | /* 163 0xa3 '£' */ | ||
1640 | 0x18, /* 00011000 */ | ||
1641 | 0x30, /* 00110000 */ | ||
1642 | 0xcc, /* 11001100 */ | ||
1643 | 0xcc, /* 11001100 */ | ||
1644 | 0xcc, /* 11001100 */ | ||
1645 | 0xcc, /* 11001100 */ | ||
1646 | 0x76, /* 01110110 */ | ||
1647 | 0x00, /* 00000000 */ | ||
1648 | |||
1649 | /* 164 0xa4 '¤' */ | ||
1650 | 0x76, /* 01110110 */ | ||
1651 | 0xdc, /* 11011100 */ | ||
1652 | 0x00, /* 00000000 */ | ||
1653 | 0xdc, /* 11011100 */ | ||
1654 | 0x66, /* 01100110 */ | ||
1655 | 0x66, /* 01100110 */ | ||
1656 | 0x66, /* 01100110 */ | ||
1657 | 0x00, /* 00000000 */ | ||
1658 | |||
1659 | /* 165 0xa5 '¥' */ | ||
1660 | 0x76, /* 01110110 */ | ||
1661 | 0xdc, /* 11011100 */ | ||
1662 | 0x00, /* 00000000 */ | ||
1663 | 0xe6, /* 11100110 */ | ||
1664 | 0xf6, /* 11110110 */ | ||
1665 | 0xde, /* 11011110 */ | ||
1666 | 0xce, /* 11001110 */ | ||
1667 | 0x00, /* 00000000 */ | ||
1668 | |||
1669 | /* 166 0xa6 '¦' */ | ||
1670 | 0x3c, /* 00111100 */ | ||
1671 | 0x6c, /* 01101100 */ | ||
1672 | 0x6c, /* 01101100 */ | ||
1673 | 0x3e, /* 00111110 */ | ||
1674 | 0x00, /* 00000000 */ | ||
1675 | 0x7e, /* 01111110 */ | ||
1676 | 0x00, /* 00000000 */ | ||
1677 | 0x00, /* 00000000 */ | ||
1678 | |||
1679 | /* 167 0xa7 '§' */ | ||
1680 | 0x38, /* 00111000 */ | ||
1681 | 0x6c, /* 01101100 */ | ||
1682 | 0x6c, /* 01101100 */ | ||
1683 | 0x38, /* 00111000 */ | ||
1684 | 0x00, /* 00000000 */ | ||
1685 | 0x7c, /* 01111100 */ | ||
1686 | 0x00, /* 00000000 */ | ||
1687 | 0x00, /* 00000000 */ | ||
1688 | |||
1689 | /* 168 0xa8 '¨' */ | ||
1690 | 0x18, /* 00011000 */ | ||
1691 | 0x00, /* 00000000 */ | ||
1692 | 0x18, /* 00011000 */ | ||
1693 | 0x18, /* 00011000 */ | ||
1694 | 0x30, /* 00110000 */ | ||
1695 | 0x63, /* 01100011 */ | ||
1696 | 0x3e, /* 00111110 */ | ||
1697 | 0x00, /* 00000000 */ | ||
1698 | |||
1699 | /* 169 0xa9 '©' */ | ||
1700 | 0x00, /* 00000000 */ | ||
1701 | 0x00, /* 00000000 */ | ||
1702 | 0x00, /* 00000000 */ | ||
1703 | 0xfe, /* 11111110 */ | ||
1704 | 0xc0, /* 11000000 */ | ||
1705 | 0xc0, /* 11000000 */ | ||
1706 | 0x00, /* 00000000 */ | ||
1707 | 0x00, /* 00000000 */ | ||
1708 | |||
1709 | /* 170 0xaa 'ª' */ | ||
1710 | 0x00, /* 00000000 */ | ||
1711 | 0x00, /* 00000000 */ | ||
1712 | 0x00, /* 00000000 */ | ||
1713 | 0xfe, /* 11111110 */ | ||
1714 | 0x06, /* 00000110 */ | ||
1715 | 0x06, /* 00000110 */ | ||
1716 | 0x00, /* 00000000 */ | ||
1717 | 0x00, /* 00000000 */ | ||
1718 | |||
1719 | /* 171 0xab '«' */ | ||
1720 | 0x63, /* 01100011 */ | ||
1721 | 0xe6, /* 11100110 */ | ||
1722 | 0x6c, /* 01101100 */ | ||
1723 | 0x7e, /* 01111110 */ | ||
1724 | 0x33, /* 00110011 */ | ||
1725 | 0x66, /* 01100110 */ | ||
1726 | 0xcc, /* 11001100 */ | ||
1727 | 0x0f, /* 00001111 */ | ||
1728 | |||
1729 | /* 172 0xac '¬' */ | ||
1730 | 0x63, /* 01100011 */ | ||
1731 | 0xe6, /* 11100110 */ | ||
1732 | 0x6c, /* 01101100 */ | ||
1733 | 0x7a, /* 01111010 */ | ||
1734 | 0x36, /* 00110110 */ | ||
1735 | 0x6a, /* 01101010 */ | ||
1736 | 0xdf, /* 11011111 */ | ||
1737 | 0x06, /* 00000110 */ | ||
1738 | |||
1739 | /* 173 0xad '' */ | ||
1740 | 0x18, /* 00011000 */ | ||
1741 | 0x00, /* 00000000 */ | ||
1742 | 0x18, /* 00011000 */ | ||
1743 | 0x18, /* 00011000 */ | ||
1744 | 0x3c, /* 00111100 */ | ||
1745 | 0x3c, /* 00111100 */ | ||
1746 | 0x18, /* 00011000 */ | ||
1747 | 0x00, /* 00000000 */ | ||
1748 | |||
1749 | /* 174 0xae '®' */ | ||
1750 | 0x00, /* 00000000 */ | ||
1751 | 0x33, /* 00110011 */ | ||
1752 | 0x66, /* 01100110 */ | ||
1753 | 0xcc, /* 11001100 */ | ||
1754 | 0x66, /* 01100110 */ | ||
1755 | 0x33, /* 00110011 */ | ||
1756 | 0x00, /* 00000000 */ | ||
1757 | 0x00, /* 00000000 */ | ||
1758 | |||
1759 | /* 175 0xaf '¯' */ | ||
1760 | 0x00, /* 00000000 */ | ||
1761 | 0xcc, /* 11001100 */ | ||
1762 | 0x66, /* 01100110 */ | ||
1763 | 0x33, /* 00110011 */ | ||
1764 | 0x66, /* 01100110 */ | ||
1765 | 0xcc, /* 11001100 */ | ||
1766 | 0x00, /* 00000000 */ | ||
1767 | 0x00, /* 00000000 */ | ||
1768 | |||
1769 | /* 176 0xb0 '°' */ | ||
1770 | 0x22, /* 00100010 */ | ||
1771 | 0x88, /* 10001000 */ | ||
1772 | 0x22, /* 00100010 */ | ||
1773 | 0x88, /* 10001000 */ | ||
1774 | 0x22, /* 00100010 */ | ||
1775 | 0x88, /* 10001000 */ | ||
1776 | 0x22, /* 00100010 */ | ||
1777 | 0x88, /* 10001000 */ | ||
1778 | |||
1779 | /* 177 0xb1 '±' */ | ||
1780 | 0x55, /* 01010101 */ | ||
1781 | 0xaa, /* 10101010 */ | ||
1782 | 0x55, /* 01010101 */ | ||
1783 | 0xaa, /* 10101010 */ | ||
1784 | 0x55, /* 01010101 */ | ||
1785 | 0xaa, /* 10101010 */ | ||
1786 | 0x55, /* 01010101 */ | ||
1787 | 0xaa, /* 10101010 */ | ||
1788 | |||
1789 | /* 178 0xb2 '²' */ | ||
1790 | 0x77, /* 01110111 */ | ||
1791 | 0xdd, /* 11011101 */ | ||
1792 | 0x77, /* 01110111 */ | ||
1793 | 0xdd, /* 11011101 */ | ||
1794 | 0x77, /* 01110111 */ | ||
1795 | 0xdd, /* 11011101 */ | ||
1796 | 0x77, /* 01110111 */ | ||
1797 | 0xdd, /* 11011101 */ | ||
1798 | |||
1799 | /* 179 0xb3 '³' */ | ||
1800 | 0x18, /* 00011000 */ | ||
1801 | 0x18, /* 00011000 */ | ||
1802 | 0x18, /* 00011000 */ | ||
1803 | 0x18, /* 00011000 */ | ||
1804 | 0x18, /* 00011000 */ | ||
1805 | 0x18, /* 00011000 */ | ||
1806 | 0x18, /* 00011000 */ | ||
1807 | 0x18, /* 00011000 */ | ||
1808 | |||
1809 | /* 180 0xb4 '´' */ | ||
1810 | 0x18, /* 00011000 */ | ||
1811 | 0x18, /* 00011000 */ | ||
1812 | 0x18, /* 00011000 */ | ||
1813 | 0x18, /* 00011000 */ | ||
1814 | 0xf8, /* 11111000 */ | ||
1815 | 0x18, /* 00011000 */ | ||
1816 | 0x18, /* 00011000 */ | ||
1817 | 0x18, /* 00011000 */ | ||
1818 | |||
1819 | /* 181 0xb5 'µ' */ | ||
1820 | 0x18, /* 00011000 */ | ||
1821 | 0x18, /* 00011000 */ | ||
1822 | 0xf8, /* 11111000 */ | ||
1823 | 0x18, /* 00011000 */ | ||
1824 | 0xf8, /* 11111000 */ | ||
1825 | 0x18, /* 00011000 */ | ||
1826 | 0x18, /* 00011000 */ | ||
1827 | 0x18, /* 00011000 */ | ||
1828 | |||
1829 | /* 182 0xb6 '¶' */ | ||
1830 | 0x36, /* 00110110 */ | ||
1831 | 0x36, /* 00110110 */ | ||
1832 | 0x36, /* 00110110 */ | ||
1833 | 0x36, /* 00110110 */ | ||
1834 | 0xf6, /* 11110110 */ | ||
1835 | 0x36, /* 00110110 */ | ||
1836 | 0x36, /* 00110110 */ | ||
1837 | 0x36, /* 00110110 */ | ||
1838 | |||
1839 | /* 183 0xb7 '·' */ | ||
1840 | 0x00, /* 00000000 */ | ||
1841 | 0x00, /* 00000000 */ | ||
1842 | 0x00, /* 00000000 */ | ||
1843 | 0x00, /* 00000000 */ | ||
1844 | 0xfe, /* 11111110 */ | ||
1845 | 0x36, /* 00110110 */ | ||
1846 | 0x36, /* 00110110 */ | ||
1847 | 0x36, /* 00110110 */ | ||
1848 | |||
1849 | /* 184 0xb8 '¸' */ | ||
1850 | 0x00, /* 00000000 */ | ||
1851 | 0x00, /* 00000000 */ | ||
1852 | 0xf8, /* 11111000 */ | ||
1853 | 0x18, /* 00011000 */ | ||
1854 | 0xf8, /* 11111000 */ | ||
1855 | 0x18, /* 00011000 */ | ||
1856 | 0x18, /* 00011000 */ | ||
1857 | 0x18, /* 00011000 */ | ||
1858 | |||
1859 | /* 185 0xb9 '¹' */ | ||
1860 | 0x36, /* 00110110 */ | ||
1861 | 0x36, /* 00110110 */ | ||
1862 | 0xf6, /* 11110110 */ | ||
1863 | 0x06, /* 00000110 */ | ||
1864 | 0xf6, /* 11110110 */ | ||
1865 | 0x36, /* 00110110 */ | ||
1866 | 0x36, /* 00110110 */ | ||
1867 | 0x36, /* 00110110 */ | ||
1868 | |||
1869 | /* 186 0xba 'º' */ | ||
1870 | 0x36, /* 00110110 */ | ||
1871 | 0x36, /* 00110110 */ | ||
1872 | 0x36, /* 00110110 */ | ||
1873 | 0x36, /* 00110110 */ | ||
1874 | 0x36, /* 00110110 */ | ||
1875 | 0x36, /* 00110110 */ | ||
1876 | 0x36, /* 00110110 */ | ||
1877 | 0x36, /* 00110110 */ | ||
1878 | |||
1879 | /* 187 0xbb '»' */ | ||
1880 | 0x00, /* 00000000 */ | ||
1881 | 0x00, /* 00000000 */ | ||
1882 | 0xfe, /* 11111110 */ | ||
1883 | 0x06, /* 00000110 */ | ||
1884 | 0xf6, /* 11110110 */ | ||
1885 | 0x36, /* 00110110 */ | ||
1886 | 0x36, /* 00110110 */ | ||
1887 | 0x36, /* 00110110 */ | ||
1888 | |||
1889 | /* 188 0xbc '¼' */ | ||
1890 | 0x36, /* 00110110 */ | ||
1891 | 0x36, /* 00110110 */ | ||
1892 | 0xf6, /* 11110110 */ | ||
1893 | 0x06, /* 00000110 */ | ||
1894 | 0xfe, /* 11111110 */ | ||
1895 | 0x00, /* 00000000 */ | ||
1896 | 0x00, /* 00000000 */ | ||
1897 | 0x00, /* 00000000 */ | ||
1898 | |||
1899 | /* 189 0xbd '½' */ | ||
1900 | 0x36, /* 00110110 */ | ||
1901 | 0x36, /* 00110110 */ | ||
1902 | 0x36, /* 00110110 */ | ||
1903 | 0x36, /* 00110110 */ | ||
1904 | 0xfe, /* 11111110 */ | ||
1905 | 0x00, /* 00000000 */ | ||
1906 | 0x00, /* 00000000 */ | ||
1907 | 0x00, /* 00000000 */ | ||
1908 | |||
1909 | /* 190 0xbe '¾' */ | ||
1910 | 0x18, /* 00011000 */ | ||
1911 | 0x18, /* 00011000 */ | ||
1912 | 0xf8, /* 11111000 */ | ||
1913 | 0x18, /* 00011000 */ | ||
1914 | 0xf8, /* 11111000 */ | ||
1915 | 0x00, /* 00000000 */ | ||
1916 | 0x00, /* 00000000 */ | ||
1917 | 0x00, /* 00000000 */ | ||
1918 | |||
1919 | /* 191 0xbf '¿' */ | ||
1920 | 0x00, /* 00000000 */ | ||
1921 | 0x00, /* 00000000 */ | ||
1922 | 0x00, /* 00000000 */ | ||
1923 | 0x00, /* 00000000 */ | ||
1924 | 0xf8, /* 11111000 */ | ||
1925 | 0x18, /* 00011000 */ | ||
1926 | 0x18, /* 00011000 */ | ||
1927 | 0x18, /* 00011000 */ | ||
1928 | |||
1929 | /* 192 0xc0 'À' */ | ||
1930 | 0x18, /* 00011000 */ | ||
1931 | 0x18, /* 00011000 */ | ||
1932 | 0x18, /* 00011000 */ | ||
1933 | 0x18, /* 00011000 */ | ||
1934 | 0x1f, /* 00011111 */ | ||
1935 | 0x00, /* 00000000 */ | ||
1936 | 0x00, /* 00000000 */ | ||
1937 | 0x00, /* 00000000 */ | ||
1938 | |||
1939 | /* 193 0xc1 'Á' */ | ||
1940 | 0x18, /* 00011000 */ | ||
1941 | 0x18, /* 00011000 */ | ||
1942 | 0x18, /* 00011000 */ | ||
1943 | 0x18, /* 00011000 */ | ||
1944 | 0xff, /* 11111111 */ | ||
1945 | 0x00, /* 00000000 */ | ||
1946 | 0x00, /* 00000000 */ | ||
1947 | 0x00, /* 00000000 */ | ||
1948 | |||
1949 | /* 194 0xc2 'Â' */ | ||
1950 | 0x00, /* 00000000 */ | ||
1951 | 0x00, /* 00000000 */ | ||
1952 | 0x00, /* 00000000 */ | ||
1953 | 0x00, /* 00000000 */ | ||
1954 | 0xff, /* 11111111 */ | ||
1955 | 0x18, /* 00011000 */ | ||
1956 | 0x18, /* 00011000 */ | ||
1957 | 0x18, /* 00011000 */ | ||
1958 | |||
1959 | /* 195 0xc3 'Ã' */ | ||
1960 | 0x18, /* 00011000 */ | ||
1961 | 0x18, /* 00011000 */ | ||
1962 | 0x18, /* 00011000 */ | ||
1963 | 0x18, /* 00011000 */ | ||
1964 | 0x1f, /* 00011111 */ | ||
1965 | 0x18, /* 00011000 */ | ||
1966 | 0x18, /* 00011000 */ | ||
1967 | 0x18, /* 00011000 */ | ||
1968 | |||
1969 | /* 196 0xc4 'Ä' */ | ||
1970 | 0x00, /* 00000000 */ | ||
1971 | 0x00, /* 00000000 */ | ||
1972 | 0x00, /* 00000000 */ | ||
1973 | 0x00, /* 00000000 */ | ||
1974 | 0xff, /* 11111111 */ | ||
1975 | 0x00, /* 00000000 */ | ||
1976 | 0x00, /* 00000000 */ | ||
1977 | 0x00, /* 00000000 */ | ||
1978 | |||
1979 | /* 197 0xc5 'Å' */ | ||
1980 | 0x18, /* 00011000 */ | ||
1981 | 0x18, /* 00011000 */ | ||
1982 | 0x18, /* 00011000 */ | ||
1983 | 0x18, /* 00011000 */ | ||
1984 | 0xff, /* 11111111 */ | ||
1985 | 0x18, /* 00011000 */ | ||
1986 | 0x18, /* 00011000 */ | ||
1987 | 0x18, /* 00011000 */ | ||
1988 | |||
1989 | /* 198 0xc6 'Æ' */ | ||
1990 | 0x18, /* 00011000 */ | ||
1991 | 0x18, /* 00011000 */ | ||
1992 | 0x1f, /* 00011111 */ | ||
1993 | 0x18, /* 00011000 */ | ||
1994 | 0x1f, /* 00011111 */ | ||
1995 | 0x18, /* 00011000 */ | ||
1996 | 0x18, /* 00011000 */ | ||
1997 | 0x18, /* 00011000 */ | ||
1998 | |||
1999 | /* 199 0xc7 'Ç' */ | ||
2000 | 0x36, /* 00110110 */ | ||
2001 | 0x36, /* 00110110 */ | ||
2002 | 0x36, /* 00110110 */ | ||
2003 | 0x36, /* 00110110 */ | ||
2004 | 0x37, /* 00110111 */ | ||
2005 | 0x36, /* 00110110 */ | ||
2006 | 0x36, /* 00110110 */ | ||
2007 | 0x36, /* 00110110 */ | ||
2008 | |||
2009 | /* 200 0xc8 'È' */ | ||
2010 | 0x36, /* 00110110 */ | ||
2011 | 0x36, /* 00110110 */ | ||
2012 | 0x37, /* 00110111 */ | ||
2013 | 0x30, /* 00110000 */ | ||
2014 | 0x3f, /* 00111111 */ | ||
2015 | 0x00, /* 00000000 */ | ||
2016 | 0x00, /* 00000000 */ | ||
2017 | 0x00, /* 00000000 */ | ||
2018 | |||
2019 | /* 201 0xc9 'É' */ | ||
2020 | 0x00, /* 00000000 */ | ||
2021 | 0x00, /* 00000000 */ | ||
2022 | 0x3f, /* 00111111 */ | ||
2023 | 0x30, /* 00110000 */ | ||
2024 | 0x37, /* 00110111 */ | ||
2025 | 0x36, /* 00110110 */ | ||
2026 | 0x36, /* 00110110 */ | ||
2027 | 0x36, /* 00110110 */ | ||
2028 | |||
2029 | /* 202 0xca 'Ê' */ | ||
2030 | 0x36, /* 00110110 */ | ||
2031 | 0x36, /* 00110110 */ | ||
2032 | 0xf7, /* 11110111 */ | ||
2033 | 0x00, /* 00000000 */ | ||
2034 | 0xff, /* 11111111 */ | ||
2035 | 0x00, /* 00000000 */ | ||
2036 | 0x00, /* 00000000 */ | ||
2037 | 0x00, /* 00000000 */ | ||
2038 | |||
2039 | /* 203 0xcb 'Ë' */ | ||
2040 | 0x00, /* 00000000 */ | ||
2041 | 0x00, /* 00000000 */ | ||
2042 | 0xff, /* 11111111 */ | ||
2043 | 0x00, /* 00000000 */ | ||
2044 | 0xf7, /* 11110111 */ | ||
2045 | 0x36, /* 00110110 */ | ||
2046 | 0x36, /* 00110110 */ | ||
2047 | 0x36, /* 00110110 */ | ||
2048 | |||
2049 | /* 204 0xcc 'Ì' */ | ||
2050 | 0x36, /* 00110110 */ | ||
2051 | 0x36, /* 00110110 */ | ||
2052 | 0x37, /* 00110111 */ | ||
2053 | 0x30, /* 00110000 */ | ||
2054 | 0x37, /* 00110111 */ | ||
2055 | 0x36, /* 00110110 */ | ||
2056 | 0x36, /* 00110110 */ | ||
2057 | 0x36, /* 00110110 */ | ||
2058 | |||
2059 | /* 205 0xcd 'Í' */ | ||
2060 | 0x00, /* 00000000 */ | ||
2061 | 0x00, /* 00000000 */ | ||
2062 | 0xff, /* 11111111 */ | ||
2063 | 0x00, /* 00000000 */ | ||
2064 | 0xff, /* 11111111 */ | ||
2065 | 0x00, /* 00000000 */ | ||
2066 | 0x00, /* 00000000 */ | ||
2067 | 0x00, /* 00000000 */ | ||
2068 | |||
2069 | /* 206 0xce 'Î' */ | ||
2070 | 0x36, /* 00110110 */ | ||
2071 | 0x36, /* 00110110 */ | ||
2072 | 0xf7, /* 11110111 */ | ||
2073 | 0x00, /* 00000000 */ | ||
2074 | 0xf7, /* 11110111 */ | ||
2075 | 0x36, /* 00110110 */ | ||
2076 | 0x36, /* 00110110 */ | ||
2077 | 0x36, /* 00110110 */ | ||
2078 | |||
2079 | /* 207 0xcf 'Ï' */ | ||
2080 | 0x18, /* 00011000 */ | ||
2081 | 0x18, /* 00011000 */ | ||
2082 | 0xff, /* 11111111 */ | ||
2083 | 0x00, /* 00000000 */ | ||
2084 | 0xff, /* 11111111 */ | ||
2085 | 0x00, /* 00000000 */ | ||
2086 | 0x00, /* 00000000 */ | ||
2087 | 0x00, /* 00000000 */ | ||
2088 | |||
2089 | /* 208 0xd0 'Ð' */ | ||
2090 | 0x36, /* 00110110 */ | ||
2091 | 0x36, /* 00110110 */ | ||
2092 | 0x36, /* 00110110 */ | ||
2093 | 0x36, /* 00110110 */ | ||
2094 | 0xff, /* 11111111 */ | ||
2095 | 0x00, /* 00000000 */ | ||
2096 | 0x00, /* 00000000 */ | ||
2097 | 0x00, /* 00000000 */ | ||
2098 | |||
2099 | /* 209 0xd1 'Ñ' */ | ||
2100 | 0x00, /* 00000000 */ | ||
2101 | 0x00, /* 00000000 */ | ||
2102 | 0xff, /* 11111111 */ | ||
2103 | 0x00, /* 00000000 */ | ||
2104 | 0xff, /* 11111111 */ | ||
2105 | 0x18, /* 00011000 */ | ||
2106 | 0x18, /* 00011000 */ | ||
2107 | 0x18, /* 00011000 */ | ||
2108 | |||
2109 | /* 210 0xd2 'Ò' */ | ||
2110 | 0x00, /* 00000000 */ | ||
2111 | 0x00, /* 00000000 */ | ||
2112 | 0x00, /* 00000000 */ | ||
2113 | 0x00, /* 00000000 */ | ||
2114 | 0xff, /* 11111111 */ | ||
2115 | 0x36, /* 00110110 */ | ||
2116 | 0x36, /* 00110110 */ | ||
2117 | 0x36, /* 00110110 */ | ||
2118 | |||
2119 | /* 211 0xd3 'Ó' */ | ||
2120 | 0x36, /* 00110110 */ | ||
2121 | 0x36, /* 00110110 */ | ||
2122 | 0x36, /* 00110110 */ | ||
2123 | 0x36, /* 00110110 */ | ||
2124 | 0x3f, /* 00111111 */ | ||
2125 | 0x00, /* 00000000 */ | ||
2126 | 0x00, /* 00000000 */ | ||
2127 | 0x00, /* 00000000 */ | ||
2128 | |||
2129 | /* 212 0xd4 'Ô' */ | ||
2130 | 0x18, /* 00011000 */ | ||
2131 | 0x18, /* 00011000 */ | ||
2132 | 0x1f, /* 00011111 */ | ||
2133 | 0x18, /* 00011000 */ | ||
2134 | 0x1f, /* 00011111 */ | ||
2135 | 0x00, /* 00000000 */ | ||
2136 | 0x00, /* 00000000 */ | ||
2137 | 0x00, /* 00000000 */ | ||
2138 | |||
2139 | /* 213 0xd5 'Õ' */ | ||
2140 | 0x00, /* 00000000 */ | ||
2141 | 0x00, /* 00000000 */ | ||
2142 | 0x1f, /* 00011111 */ | ||
2143 | 0x18, /* 00011000 */ | ||
2144 | 0x1f, /* 00011111 */ | ||
2145 | 0x18, /* 00011000 */ | ||
2146 | 0x18, /* 00011000 */ | ||
2147 | 0x18, /* 00011000 */ | ||
2148 | |||
2149 | /* 214 0xd6 'Ö' */ | ||
2150 | 0x00, /* 00000000 */ | ||
2151 | 0x00, /* 00000000 */ | ||
2152 | 0x00, /* 00000000 */ | ||
2153 | 0x00, /* 00000000 */ | ||
2154 | 0x3f, /* 00111111 */ | ||
2155 | 0x36, /* 00110110 */ | ||
2156 | 0x36, /* 00110110 */ | ||
2157 | 0x36, /* 00110110 */ | ||
2158 | |||
2159 | /* 215 0xd7 '×' */ | ||
2160 | 0x36, /* 00110110 */ | ||
2161 | 0x36, /* 00110110 */ | ||
2162 | 0x36, /* 00110110 */ | ||
2163 | 0x36, /* 00110110 */ | ||
2164 | 0xff, /* 11111111 */ | ||
2165 | 0x36, /* 00110110 */ | ||
2166 | 0x36, /* 00110110 */ | ||
2167 | 0x36, /* 00110110 */ | ||
2168 | |||
2169 | /* 216 0xd8 'Ø' */ | ||
2170 | 0x18, /* 00011000 */ | ||
2171 | 0x18, /* 00011000 */ | ||
2172 | 0xff, /* 11111111 */ | ||
2173 | 0x18, /* 00011000 */ | ||
2174 | 0xff, /* 11111111 */ | ||
2175 | 0x18, /* 00011000 */ | ||
2176 | 0x18, /* 00011000 */ | ||
2177 | 0x18, /* 00011000 */ | ||
2178 | |||
2179 | /* 217 0xd9 'Ù' */ | ||
2180 | 0x18, /* 00011000 */ | ||
2181 | 0x18, /* 00011000 */ | ||
2182 | 0x18, /* 00011000 */ | ||
2183 | 0x18, /* 00011000 */ | ||
2184 | 0xf8, /* 11111000 */ | ||
2185 | 0x00, /* 00000000 */ | ||
2186 | 0x00, /* 00000000 */ | ||
2187 | 0x00, /* 00000000 */ | ||
2188 | |||
2189 | /* 218 0xda 'Ú' */ | ||
2190 | 0x00, /* 00000000 */ | ||
2191 | 0x00, /* 00000000 */ | ||
2192 | 0x00, /* 00000000 */ | ||
2193 | 0x00, /* 00000000 */ | ||
2194 | 0x1f, /* 00011111 */ | ||
2195 | 0x18, /* 00011000 */ | ||
2196 | 0x18, /* 00011000 */ | ||
2197 | 0x18, /* 00011000 */ | ||
2198 | |||
2199 | /* 219 0xdb 'Û' */ | ||
2200 | 0xff, /* 11111111 */ | ||
2201 | 0xff, /* 11111111 */ | ||
2202 | 0xff, /* 11111111 */ | ||
2203 | 0xff, /* 11111111 */ | ||
2204 | 0xff, /* 11111111 */ | ||
2205 | 0xff, /* 11111111 */ | ||
2206 | 0xff, /* 11111111 */ | ||
2207 | 0xff, /* 11111111 */ | ||
2208 | |||
2209 | /* 220 0xdc 'Ü' */ | ||
2210 | 0x00, /* 00000000 */ | ||
2211 | 0x00, /* 00000000 */ | ||
2212 | 0x00, /* 00000000 */ | ||
2213 | 0x00, /* 00000000 */ | ||
2214 | 0xff, /* 11111111 */ | ||
2215 | 0xff, /* 11111111 */ | ||
2216 | 0xff, /* 11111111 */ | ||
2217 | 0xff, /* 11111111 */ | ||
2218 | |||
2219 | /* 221 0xdd 'Ý' */ | ||
2220 | 0xf0, /* 11110000 */ | ||
2221 | 0xf0, /* 11110000 */ | ||
2222 | 0xf0, /* 11110000 */ | ||
2223 | 0xf0, /* 11110000 */ | ||
2224 | 0xf0, /* 11110000 */ | ||
2225 | 0xf0, /* 11110000 */ | ||
2226 | 0xf0, /* 11110000 */ | ||
2227 | 0xf0, /* 11110000 */ | ||
2228 | |||
2229 | /* 222 0xde 'Þ' */ | ||
2230 | 0x0f, /* 00001111 */ | ||
2231 | 0x0f, /* 00001111 */ | ||
2232 | 0x0f, /* 00001111 */ | ||
2233 | 0x0f, /* 00001111 */ | ||
2234 | 0x0f, /* 00001111 */ | ||
2235 | 0x0f, /* 00001111 */ | ||
2236 | 0x0f, /* 00001111 */ | ||
2237 | 0x0f, /* 00001111 */ | ||
2238 | |||
2239 | /* 223 0xdf 'ß' */ | ||
2240 | 0xff, /* 11111111 */ | ||
2241 | 0xff, /* 11111111 */ | ||
2242 | 0xff, /* 11111111 */ | ||
2243 | 0xff, /* 11111111 */ | ||
2244 | 0x00, /* 00000000 */ | ||
2245 | 0x00, /* 00000000 */ | ||
2246 | 0x00, /* 00000000 */ | ||
2247 | 0x00, /* 00000000 */ | ||
2248 | |||
2249 | /* 224 0xe0 'à' */ | ||
2250 | 0x00, /* 00000000 */ | ||
2251 | 0x00, /* 00000000 */ | ||
2252 | 0x76, /* 01110110 */ | ||
2253 | 0xdc, /* 11011100 */ | ||
2254 | 0xc8, /* 11001000 */ | ||
2255 | 0xdc, /* 11011100 */ | ||
2256 | 0x76, /* 01110110 */ | ||
2257 | 0x00, /* 00000000 */ | ||
2258 | |||
2259 | /* 225 0xe1 'á' */ | ||
2260 | 0x78, /* 01111000 */ | ||
2261 | 0xcc, /* 11001100 */ | ||
2262 | 0xcc, /* 11001100 */ | ||
2263 | 0xd8, /* 11011000 */ | ||
2264 | 0xcc, /* 11001100 */ | ||
2265 | 0xc6, /* 11000110 */ | ||
2266 | 0xcc, /* 11001100 */ | ||
2267 | 0x00, /* 00000000 */ | ||
2268 | |||
2269 | /* 226 0xe2 'â' */ | ||
2270 | 0xfe, /* 11111110 */ | ||
2271 | 0xc6, /* 11000110 */ | ||
2272 | 0xc0, /* 11000000 */ | ||
2273 | 0xc0, /* 11000000 */ | ||
2274 | 0xc0, /* 11000000 */ | ||
2275 | 0xc0, /* 11000000 */ | ||
2276 | 0xc0, /* 11000000 */ | ||
2277 | 0x00, /* 00000000 */ | ||
2278 | |||
2279 | /* 227 0xe3 'ã' */ | ||
2280 | 0x00, /* 00000000 */ | ||
2281 | 0x00, /* 00000000 */ | ||
2282 | 0xfe, /* 11111110 */ | ||
2283 | 0x6c, /* 01101100 */ | ||
2284 | 0x6c, /* 01101100 */ | ||
2285 | 0x6c, /* 01101100 */ | ||
2286 | 0x6c, /* 01101100 */ | ||
2287 | 0x00, /* 00000000 */ | ||
2288 | |||
2289 | /* 228 0xe4 'ä' */ | ||
2290 | 0xfe, /* 11111110 */ | ||
2291 | 0xc6, /* 11000110 */ | ||
2292 | 0x60, /* 01100000 */ | ||
2293 | 0x30, /* 00110000 */ | ||
2294 | 0x60, /* 01100000 */ | ||
2295 | 0xc6, /* 11000110 */ | ||
2296 | 0xfe, /* 11111110 */ | ||
2297 | 0x00, /* 00000000 */ | ||
2298 | |||
2299 | /* 229 0xe5 'å' */ | ||
2300 | 0x00, /* 00000000 */ | ||
2301 | 0x00, /* 00000000 */ | ||
2302 | 0x7e, /* 01111110 */ | ||
2303 | 0xd8, /* 11011000 */ | ||
2304 | 0xd8, /* 11011000 */ | ||
2305 | 0xd8, /* 11011000 */ | ||
2306 | 0x70, /* 01110000 */ | ||
2307 | 0x00, /* 00000000 */ | ||
2308 | |||
2309 | /* 230 0xe6 'æ' */ | ||
2310 | 0x00, /* 00000000 */ | ||
2311 | 0x00, /* 00000000 */ | ||
2312 | 0x66, /* 01100110 */ | ||
2313 | 0x66, /* 01100110 */ | ||
2314 | 0x66, /* 01100110 */ | ||
2315 | 0x66, /* 01100110 */ | ||
2316 | 0x7c, /* 01111100 */ | ||
2317 | 0xc0, /* 11000000 */ | ||
2318 | |||
2319 | /* 231 0xe7 'ç' */ | ||
2320 | 0x00, /* 00000000 */ | ||
2321 | 0x76, /* 01110110 */ | ||
2322 | 0xdc, /* 11011100 */ | ||
2323 | 0x18, /* 00011000 */ | ||
2324 | 0x18, /* 00011000 */ | ||
2325 | 0x18, /* 00011000 */ | ||
2326 | 0x18, /* 00011000 */ | ||
2327 | 0x00, /* 00000000 */ | ||
2328 | |||
2329 | /* 232 0xe8 'è' */ | ||
2330 | 0x7e, /* 01111110 */ | ||
2331 | 0x18, /* 00011000 */ | ||
2332 | 0x3c, /* 00111100 */ | ||
2333 | 0x66, /* 01100110 */ | ||
2334 | 0x66, /* 01100110 */ | ||
2335 | 0x3c, /* 00111100 */ | ||
2336 | 0x18, /* 00011000 */ | ||
2337 | 0x7e, /* 01111110 */ | ||
2338 | |||
2339 | /* 233 0xe9 'é' */ | ||
2340 | 0x38, /* 00111000 */ | ||
2341 | 0x6c, /* 01101100 */ | ||
2342 | 0xc6, /* 11000110 */ | ||
2343 | 0xfe, /* 11111110 */ | ||
2344 | 0xc6, /* 11000110 */ | ||
2345 | 0x6c, /* 01101100 */ | ||
2346 | 0x38, /* 00111000 */ | ||
2347 | 0x00, /* 00000000 */ | ||
2348 | |||
2349 | /* 234 0xea 'ê' */ | ||
2350 | 0x38, /* 00111000 */ | ||
2351 | 0x6c, /* 01101100 */ | ||
2352 | 0xc6, /* 11000110 */ | ||
2353 | 0xc6, /* 11000110 */ | ||
2354 | 0x6c, /* 01101100 */ | ||
2355 | 0x6c, /* 01101100 */ | ||
2356 | 0xee, /* 11101110 */ | ||
2357 | 0x00, /* 00000000 */ | ||
2358 | |||
2359 | /* 235 0xeb 'ë' */ | ||
2360 | 0x0e, /* 00001110 */ | ||
2361 | 0x18, /* 00011000 */ | ||
2362 | 0x0c, /* 00001100 */ | ||
2363 | 0x3e, /* 00111110 */ | ||
2364 | 0x66, /* 01100110 */ | ||
2365 | 0x66, /* 01100110 */ | ||
2366 | 0x3c, /* 00111100 */ | ||
2367 | 0x00, /* 00000000 */ | ||
2368 | |||
2369 | /* 236 0xec 'ì' */ | ||
2370 | 0x00, /* 00000000 */ | ||
2371 | 0x00, /* 00000000 */ | ||
2372 | 0x7e, /* 01111110 */ | ||
2373 | 0xdb, /* 11011011 */ | ||
2374 | 0xdb, /* 11011011 */ | ||
2375 | 0x7e, /* 01111110 */ | ||
2376 | 0x00, /* 00000000 */ | ||
2377 | 0x00, /* 00000000 */ | ||
2378 | |||
2379 | /* 237 0xed 'í' */ | ||
2380 | 0x06, /* 00000110 */ | ||
2381 | 0x0c, /* 00001100 */ | ||
2382 | 0x7e, /* 01111110 */ | ||
2383 | 0xdb, /* 11011011 */ | ||
2384 | 0xdb, /* 11011011 */ | ||
2385 | 0x7e, /* 01111110 */ | ||
2386 | 0x60, /* 01100000 */ | ||
2387 | 0xc0, /* 11000000 */ | ||
2388 | |||
2389 | /* 238 0xee 'î' */ | ||
2390 | 0x1e, /* 00011110 */ | ||
2391 | 0x30, /* 00110000 */ | ||
2392 | 0x60, /* 01100000 */ | ||
2393 | 0x7e, /* 01111110 */ | ||
2394 | 0x60, /* 01100000 */ | ||
2395 | 0x30, /* 00110000 */ | ||
2396 | 0x1e, /* 00011110 */ | ||
2397 | 0x00, /* 00000000 */ | ||
2398 | |||
2399 | /* 239 0xef 'ï' */ | ||
2400 | 0x00, /* 00000000 */ | ||
2401 | 0x7c, /* 01111100 */ | ||
2402 | 0xc6, /* 11000110 */ | ||
2403 | 0xc6, /* 11000110 */ | ||
2404 | 0xc6, /* 11000110 */ | ||
2405 | 0xc6, /* 11000110 */ | ||
2406 | 0xc6, /* 11000110 */ | ||
2407 | 0x00, /* 00000000 */ | ||
2408 | |||
2409 | /* 240 0xf0 'ð' */ | ||
2410 | 0x00, /* 00000000 */ | ||
2411 | 0xfe, /* 11111110 */ | ||
2412 | 0x00, /* 00000000 */ | ||
2413 | 0xfe, /* 11111110 */ | ||
2414 | 0x00, /* 00000000 */ | ||
2415 | 0xfe, /* 11111110 */ | ||
2416 | 0x00, /* 00000000 */ | ||
2417 | 0x00, /* 00000000 */ | ||
2418 | |||
2419 | /* 241 0xf1 'ñ' */ | ||
2420 | 0x18, /* 00011000 */ | ||
2421 | 0x18, /* 00011000 */ | ||
2422 | 0x7e, /* 01111110 */ | ||
2423 | 0x18, /* 00011000 */ | ||
2424 | 0x18, /* 00011000 */ | ||
2425 | 0x00, /* 00000000 */ | ||
2426 | 0x7e, /* 01111110 */ | ||
2427 | 0x00, /* 00000000 */ | ||
2428 | |||
2429 | /* 242 0xf2 'ò' */ | ||
2430 | 0x30, /* 00110000 */ | ||
2431 | 0x18, /* 00011000 */ | ||
2432 | 0x0c, /* 00001100 */ | ||
2433 | 0x18, /* 00011000 */ | ||
2434 | 0x30, /* 00110000 */ | ||
2435 | 0x00, /* 00000000 */ | ||
2436 | 0x7e, /* 01111110 */ | ||
2437 | 0x00, /* 00000000 */ | ||
2438 | |||
2439 | /* 243 0xf3 'ó' */ | ||
2440 | 0x0c, /* 00001100 */ | ||
2441 | 0x18, /* 00011000 */ | ||
2442 | 0x30, /* 00110000 */ | ||
2443 | 0x18, /* 00011000 */ | ||
2444 | 0x0c, /* 00001100 */ | ||
2445 | 0x00, /* 00000000 */ | ||
2446 | 0x7e, /* 01111110 */ | ||
2447 | 0x00, /* 00000000 */ | ||
2448 | |||
2449 | /* 244 0xf4 'ô' */ | ||
2450 | 0x0e, /* 00001110 */ | ||
2451 | 0x1b, /* 00011011 */ | ||
2452 | 0x1b, /* 00011011 */ | ||
2453 | 0x18, /* 00011000 */ | ||
2454 | 0x18, /* 00011000 */ | ||
2455 | 0x18, /* 00011000 */ | ||
2456 | 0x18, /* 00011000 */ | ||
2457 | 0x18, /* 00011000 */ | ||
2458 | |||
2459 | /* 245 0xf5 'õ' */ | ||
2460 | 0x18, /* 00011000 */ | ||
2461 | 0x18, /* 00011000 */ | ||
2462 | 0x18, /* 00011000 */ | ||
2463 | 0x18, /* 00011000 */ | ||
2464 | 0x18, /* 00011000 */ | ||
2465 | 0xd8, /* 11011000 */ | ||
2466 | 0xd8, /* 11011000 */ | ||
2467 | 0x70, /* 01110000 */ | ||
2468 | |||
2469 | /* 246 0xf6 'ö' */ | ||
2470 | 0x00, /* 00000000 */ | ||
2471 | 0x18, /* 00011000 */ | ||
2472 | 0x00, /* 00000000 */ | ||
2473 | 0x7e, /* 01111110 */ | ||
2474 | 0x00, /* 00000000 */ | ||
2475 | 0x18, /* 00011000 */ | ||
2476 | 0x00, /* 00000000 */ | ||
2477 | 0x00, /* 00000000 */ | ||
2478 | |||
2479 | /* 247 0xf7 '÷' */ | ||
2480 | 0x00, /* 00000000 */ | ||
2481 | 0x76, /* 01110110 */ | ||
2482 | 0xdc, /* 11011100 */ | ||
2483 | 0x00, /* 00000000 */ | ||
2484 | 0x76, /* 01110110 */ | ||
2485 | 0xdc, /* 11011100 */ | ||
2486 | 0x00, /* 00000000 */ | ||
2487 | 0x00, /* 00000000 */ | ||
2488 | |||
2489 | /* 248 0xf8 'ø' */ | ||
2490 | 0x38, /* 00111000 */ | ||
2491 | 0x6c, /* 01101100 */ | ||
2492 | 0x6c, /* 01101100 */ | ||
2493 | 0x38, /* 00111000 */ | ||
2494 | 0x00, /* 00000000 */ | ||
2495 | 0x00, /* 00000000 */ | ||
2496 | 0x00, /* 00000000 */ | ||
2497 | 0x00, /* 00000000 */ | ||
2498 | |||
2499 | /* 249 0xf9 'ù' */ | ||
2500 | 0x00, /* 00000000 */ | ||
2501 | 0x00, /* 00000000 */ | ||
2502 | 0x00, /* 00000000 */ | ||
2503 | 0x18, /* 00011000 */ | ||
2504 | 0x18, /* 00011000 */ | ||
2505 | 0x00, /* 00000000 */ | ||
2506 | 0x00, /* 00000000 */ | ||
2507 | 0x00, /* 00000000 */ | ||
2508 | |||
2509 | /* 250 0xfa 'ú' */ | ||
2510 | 0x00, /* 00000000 */ | ||
2511 | 0x00, /* 00000000 */ | ||
2512 | 0x00, /* 00000000 */ | ||
2513 | 0x18, /* 00011000 */ | ||
2514 | 0x00, /* 00000000 */ | ||
2515 | 0x00, /* 00000000 */ | ||
2516 | 0x00, /* 00000000 */ | ||
2517 | 0x00, /* 00000000 */ | ||
2518 | |||
2519 | /* 251 0xfb 'û' */ | ||
2520 | 0x0f, /* 00001111 */ | ||
2521 | 0x0c, /* 00001100 */ | ||
2522 | 0x0c, /* 00001100 */ | ||
2523 | 0x0c, /* 00001100 */ | ||
2524 | 0xec, /* 11101100 */ | ||
2525 | 0x6c, /* 01101100 */ | ||
2526 | 0x3c, /* 00111100 */ | ||
2527 | 0x1c, /* 00011100 */ | ||
2528 | |||
2529 | /* 252 0xfc 'ü' */ | ||
2530 | 0x6c, /* 01101100 */ | ||
2531 | 0x36, /* 00110110 */ | ||
2532 | 0x36, /* 00110110 */ | ||
2533 | 0x36, /* 00110110 */ | ||
2534 | 0x36, /* 00110110 */ | ||
2535 | 0x00, /* 00000000 */ | ||
2536 | 0x00, /* 00000000 */ | ||
2537 | 0x00, /* 00000000 */ | ||
2538 | |||
2539 | /* 253 0xfd 'ý' */ | ||
2540 | 0x78, /* 01111000 */ | ||
2541 | 0x0c, /* 00001100 */ | ||
2542 | 0x18, /* 00011000 */ | ||
2543 | 0x30, /* 00110000 */ | ||
2544 | 0x7c, /* 01111100 */ | ||
2545 | 0x00, /* 00000000 */ | ||
2546 | 0x00, /* 00000000 */ | ||
2547 | 0x00, /* 00000000 */ | ||
2548 | |||
2549 | /* 254 0xfe 'þ' */ | ||
2550 | 0x00, /* 00000000 */ | ||
2551 | 0x00, /* 00000000 */ | ||
2552 | 0x3c, /* 00111100 */ | ||
2553 | 0x3c, /* 00111100 */ | ||
2554 | 0x3c, /* 00111100 */ | ||
2555 | 0x3c, /* 00111100 */ | ||
2556 | 0x00, /* 00000000 */ | ||
2557 | 0x00, /* 00000000 */ | ||
2558 | |||
2559 | /* 255 0xff 'ÿ' */ | ||
2560 | 0x00, /* 00000000 */ | ||
2561 | 0x00, /* 00000000 */ | ||
2562 | 0x00, /* 00000000 */ | ||
2563 | 0x00, /* 00000000 */ | ||
2564 | 0x00, /* 00000000 */ | ||
2565 | 0x00, /* 00000000 */ | ||
2566 | 0x00, /* 00000000 */ | ||
2567 | 0x00, /* 00000000 */ | ||
2568 | |||
2569 | }; | ||
2570 | |||
diff --git a/kms++util/src/opts.cpp b/kms++util/src/opts.cpp new file mode 100644 index 0000000..afef452 --- /dev/null +++ b/kms++util/src/opts.cpp | |||
@@ -0,0 +1,117 @@ | |||
1 | #include <algorithm> | ||
2 | |||
3 | #include <unistd.h> | ||
4 | #include <getopt.h> | ||
5 | |||
6 | #include <kms++util/opts.h> | ||
7 | |||
8 | using namespace std; | ||
9 | |||
10 | Option::Option(const string& str, function<void()> func) | ||
11 | : m_void_func(func) | ||
12 | { | ||
13 | parse(str); | ||
14 | } | ||
15 | |||
16 | Option::Option(const string& str, function<void(const string)> func) | ||
17 | : m_func(func) | ||
18 | { | ||
19 | parse(str); | ||
20 | } | ||
21 | |||
22 | void Option::parse(const string& str) | ||
23 | { | ||
24 | auto iend = str.end(); | ||
25 | if (*(iend - 1) == '=') { | ||
26 | iend--; | ||
27 | m_has_arg = 1; | ||
28 | } else if (*(iend - 1) == '?') { | ||
29 | iend--; | ||
30 | m_has_arg = 2; | ||
31 | } else { | ||
32 | m_has_arg = 0; | ||
33 | } | ||
34 | |||
35 | auto isplit = find(str.begin(), iend, '|'); | ||
36 | |||
37 | if (isplit != str.begin()) | ||
38 | m_short = str[0]; | ||
39 | else | ||
40 | m_short = 0; | ||
41 | |||
42 | if (isplit != iend) | ||
43 | m_long = string(isplit + 1, iend); | ||
44 | } | ||
45 | |||
46 | OptionSet::OptionSet(initializer_list<Option> il) | ||
47 | : m_opts(il) | ||
48 | { | ||
49 | } | ||
50 | |||
51 | void OptionSet::parse(int argc, char** argv) | ||
52 | { | ||
53 | string shortopts = ":"; | ||
54 | vector<struct option> longopts; | ||
55 | |||
56 | for (unsigned opt_idx = 0; opt_idx < m_opts.size(); ++opt_idx) { | ||
57 | const Option& o = m_opts[opt_idx]; | ||
58 | |||
59 | if (o.m_short != 0) { | ||
60 | shortopts.push_back(o.m_short); | ||
61 | if (o.m_has_arg == 1) | ||
62 | shortopts.push_back(':'); | ||
63 | else if (o.m_has_arg == 2) | ||
64 | shortopts.append("::"); | ||
65 | } | ||
66 | |||
67 | if (!o.m_long.empty()) { | ||
68 | struct option copt; | ||
69 | copt.name = o.m_long.c_str(); | ||
70 | copt.has_arg = o.m_has_arg; | ||
71 | copt.flag = 0; | ||
72 | copt.val = opt_idx + 1000; | ||
73 | longopts.push_back(copt); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | longopts.push_back(option {}); | ||
78 | |||
79 | while (1) { | ||
80 | int long_idx = 0; | ||
81 | int c = getopt_long(argc, argv, shortopts.c_str(), | ||
82 | longopts.data(), &long_idx); | ||
83 | if (c == -1) | ||
84 | break; | ||
85 | |||
86 | if (c == '?') | ||
87 | throw std::invalid_argument(string("Unrecognized option ") + argv[optind - 1]); | ||
88 | |||
89 | if (c == ':') { | ||
90 | const Option& o = find_opt(optopt); | ||
91 | if (optopt < 256) | ||
92 | throw std::invalid_argument(string("Missing argument to -") + o.m_short); | ||
93 | else | ||
94 | throw std::invalid_argument(string("Missing argument to --") + o.m_long); | ||
95 | } | ||
96 | |||
97 | string sarg = { optarg ?: "" }; | ||
98 | |||
99 | const Option& opt = find_opt(c); | ||
100 | |||
101 | if (opt.m_func) | ||
102 | opt.m_func(sarg); | ||
103 | else | ||
104 | opt.m_void_func(); | ||
105 | } | ||
106 | |||
107 | for (int i = optind; i < argc; ++i) | ||
108 | m_params.push_back(argv[i]); | ||
109 | } | ||
110 | |||
111 | const Option& OptionSet::find_opt(int c) | ||
112 | { | ||
113 | if (c < 256) | ||
114 | return *find_if(m_opts.begin(), m_opts.end(), [c](const Option& o) { return o.m_short == c; }); | ||
115 | else | ||
116 | return m_opts[c - 1000]; | ||
117 | } | ||
diff --git a/kms++util/src/resourcemanager.cpp b/kms++util/src/resourcemanager.cpp new file mode 100644 index 0000000..9a8a66b --- /dev/null +++ b/kms++util/src/resourcemanager.cpp | |||
@@ -0,0 +1,206 @@ | |||
1 | #include <kms++util/resourcemanager.h> | ||
2 | #include <algorithm> | ||
3 | #include <kms++util/strhelpers.h> | ||
4 | |||
5 | using namespace kms; | ||
6 | using namespace std; | ||
7 | |||
8 | ResourceManager::ResourceManager(Card& card) | ||
9 | : m_card(card) | ||
10 | { | ||
11 | } | ||
12 | |||
13 | void ResourceManager::reset() | ||
14 | { | ||
15 | m_reserved_connectors.clear(); | ||
16 | m_reserved_crtcs.clear(); | ||
17 | m_reserved_planes.clear(); | ||
18 | } | ||
19 | |||
20 | static Connector* find_connector(Card& card, const set<Connector*> reserved) | ||
21 | { | ||
22 | for (Connector* conn : card.get_connectors()) { | ||
23 | if (!conn->connected()) | ||
24 | continue; | ||
25 | |||
26 | if (reserved.count(conn)) | ||
27 | continue; | ||
28 | |||
29 | return conn; | ||
30 | } | ||
31 | |||
32 | return nullptr; | ||
33 | } | ||
34 | |||
35 | static Connector* resolve_connector(Card& card, const string& name, const set<Connector*> reserved) | ||
36 | { | ||
37 | auto connectors = card.get_connectors(); | ||
38 | |||
39 | if (name[0] == '@') { | ||
40 | char* endptr; | ||
41 | unsigned id = strtoul(name.c_str() + 1, &endptr, 10); | ||
42 | if (*endptr == 0) { | ||
43 | Connector* c = card.get_connector(id); | ||
44 | |||
45 | if (!c || reserved.count(c)) | ||
46 | return nullptr; | ||
47 | |||
48 | return c; | ||
49 | } | ||
50 | } else { | ||
51 | char* endptr; | ||
52 | unsigned idx = strtoul(name.c_str(), &endptr, 10); | ||
53 | if (*endptr == 0) { | ||
54 | if (idx >= connectors.size()) | ||
55 | return nullptr; | ||
56 | |||
57 | Connector* c = connectors[idx]; | ||
58 | |||
59 | if (reserved.count(c)) | ||
60 | return nullptr; | ||
61 | |||
62 | return c; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | for (Connector* conn : connectors) { | ||
67 | if (to_lower(conn->fullname()).find(to_lower(name)) == string::npos) | ||
68 | continue; | ||
69 | |||
70 | if (reserved.count(conn)) | ||
71 | continue; | ||
72 | |||
73 | return conn; | ||
74 | } | ||
75 | |||
76 | return nullptr; | ||
77 | } | ||
78 | |||
79 | Connector* ResourceManager::reserve_connector(const string& name) | ||
80 | { | ||
81 | Connector* conn; | ||
82 | |||
83 | if (name.empty()) | ||
84 | conn = find_connector(m_card, m_reserved_connectors); | ||
85 | else | ||
86 | conn = resolve_connector(m_card, name, m_reserved_connectors); | ||
87 | |||
88 | if (!conn) | ||
89 | return nullptr; | ||
90 | |||
91 | m_reserved_connectors.insert(conn); | ||
92 | return conn; | ||
93 | } | ||
94 | |||
95 | Connector* ResourceManager::reserve_connector(Connector* conn) | ||
96 | { | ||
97 | if (!conn) | ||
98 | return nullptr; | ||
99 | |||
100 | if (m_reserved_connectors.count(conn)) | ||
101 | return nullptr; | ||
102 | |||
103 | m_reserved_connectors.insert(conn); | ||
104 | return conn; | ||
105 | } | ||
106 | |||
107 | Crtc* ResourceManager::reserve_crtc(Connector* conn) | ||
108 | { | ||
109 | if (!conn) | ||
110 | return nullptr; | ||
111 | |||
112 | if (Crtc* crtc = conn->get_current_crtc()) { | ||
113 | m_reserved_crtcs.insert(crtc); | ||
114 | return crtc; | ||
115 | } | ||
116 | |||
117 | for (Crtc* crtc : conn->get_possible_crtcs()) { | ||
118 | if (m_reserved_crtcs.count(crtc)) | ||
119 | continue; | ||
120 | |||
121 | m_reserved_crtcs.insert(crtc); | ||
122 | return crtc; | ||
123 | } | ||
124 | |||
125 | return nullptr; | ||
126 | } | ||
127 | |||
128 | Crtc* ResourceManager::reserve_crtc(Crtc* crtc) | ||
129 | { | ||
130 | if (!crtc) | ||
131 | return nullptr; | ||
132 | |||
133 | if (m_reserved_crtcs.count(crtc)) | ||
134 | return nullptr; | ||
135 | |||
136 | m_reserved_crtcs.insert(crtc); | ||
137 | |||
138 | return crtc; | ||
139 | } | ||
140 | |||
141 | Plane* ResourceManager::reserve_plane(Crtc* crtc, PlaneType type, PixelFormat format) | ||
142 | { | ||
143 | if (!crtc) | ||
144 | return nullptr; | ||
145 | |||
146 | for (Plane* plane : crtc->get_possible_planes()) { | ||
147 | if (plane->plane_type() != type) | ||
148 | continue; | ||
149 | |||
150 | if (format != PixelFormat::Undefined && !plane->supports_format(format)) | ||
151 | continue; | ||
152 | |||
153 | if (m_reserved_planes.count(plane)) | ||
154 | continue; | ||
155 | |||
156 | m_reserved_planes.insert(plane); | ||
157 | return plane; | ||
158 | } | ||
159 | |||
160 | return nullptr; | ||
161 | } | ||
162 | |||
163 | Plane* ResourceManager::reserve_plane(Plane* plane) | ||
164 | { | ||
165 | if (!plane) | ||
166 | return nullptr; | ||
167 | |||
168 | if (m_reserved_planes.count(plane)) | ||
169 | return nullptr; | ||
170 | |||
171 | m_reserved_planes.insert(plane); | ||
172 | |||
173 | return plane; | ||
174 | } | ||
175 | |||
176 | Plane* ResourceManager::reserve_generic_plane(Crtc* crtc, PixelFormat format) | ||
177 | { | ||
178 | if (!crtc) | ||
179 | return nullptr; | ||
180 | |||
181 | for (Plane* plane : crtc->get_possible_planes()) { | ||
182 | if (plane->plane_type() == PlaneType::Cursor) | ||
183 | continue; | ||
184 | |||
185 | if (format != PixelFormat::Undefined && !plane->supports_format(format)) | ||
186 | continue; | ||
187 | |||
188 | if (m_reserved_planes.count(plane)) | ||
189 | continue; | ||
190 | |||
191 | m_reserved_planes.insert(plane); | ||
192 | return plane; | ||
193 | } | ||
194 | |||
195 | return nullptr; | ||
196 | } | ||
197 | |||
198 | Plane* ResourceManager::reserve_primary_plane(Crtc* crtc, PixelFormat format) | ||
199 | { | ||
200 | return reserve_plane(crtc, PlaneType::Primary, format); | ||
201 | } | ||
202 | |||
203 | Plane* ResourceManager::reserve_overlay_plane(Crtc* crtc, PixelFormat format) | ||
204 | { | ||
205 | return reserve_plane(crtc, PlaneType::Overlay, format); | ||
206 | } | ||
diff --git a/kms++util/src/strhelpers.cpp b/kms++util/src/strhelpers.cpp new file mode 100644 index 0000000..f59bb6d --- /dev/null +++ b/kms++util/src/strhelpers.cpp | |||
@@ -0,0 +1,27 @@ | |||
1 | #include <kms++util/strhelpers.h> | ||
2 | |||
3 | #include <algorithm> | ||
4 | #include <stdarg.h> | ||
5 | |||
6 | using namespace std; | ||
7 | |||
8 | string to_lower(const string& str) | ||
9 | { | ||
10 | string data = str; | ||
11 | transform(data.begin(), data.end(), data.begin(), ::tolower); | ||
12 | return data; | ||
13 | } | ||
14 | |||
15 | string sformat(const char *fmt, ...) | ||
16 | { | ||
17 | static char s_format_buf[1024]; | ||
18 | |||
19 | va_list args; | ||
20 | va_start(args, fmt); | ||
21 | |||
22 | vsnprintf(s_format_buf, sizeof(s_format_buf), fmt, args); | ||
23 | |||
24 | va_end(args); | ||
25 | |||
26 | return string(s_format_buf); | ||
27 | } | ||
diff --git a/kms++util/src/testpat.cpp b/kms++util/src/testpat.cpp new file mode 100644 index 0000000..cf43d00 --- /dev/null +++ b/kms++util/src/testpat.cpp | |||
@@ -0,0 +1,199 @@ | |||
1 | |||
2 | //#define DRAW_PERF_PRINT | ||
3 | |||
4 | #include <cstring> | ||
5 | #include <cassert> | ||
6 | #include <thread> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | #include <kms++util/kms++util.h> | ||
10 | |||
11 | using namespace std; | ||
12 | |||
13 | namespace kms | ||
14 | { | ||
15 | |||
16 | static RGB get_test_pattern_pixel(IFramebuffer& fb, unsigned x, unsigned y) | ||
17 | { | ||
18 | const unsigned w = fb.width(); | ||
19 | const unsigned h = fb.height(); | ||
20 | |||
21 | const unsigned mw = 20; | ||
22 | |||
23 | const unsigned xm1 = mw; | ||
24 | const unsigned xm2 = w - mw - 1; | ||
25 | const unsigned ym1 = mw; | ||
26 | const unsigned ym2 = h - mw - 1; | ||
27 | |||
28 | // white margin lines | ||
29 | if (x == xm1 || x == xm2 || y == ym1 || y == ym2) | ||
30 | return RGB(255, 255, 255); | ||
31 | // white box in top left corner | ||
32 | else if (x < xm1 && y < ym1) | ||
33 | return RGB(255, 255, 255); | ||
34 | // white box outlines to corners | ||
35 | else if ((x == 0 || x == w - 1) && (y < ym1 || y > ym2)) | ||
36 | return RGB(255, 255, 255); | ||
37 | // white box outlines to corners | ||
38 | else if ((y == 0 || y == h - 1) && (x < xm1 || x > xm2)) | ||
39 | return RGB(255, 255, 255); | ||
40 | // blue bar on the left | ||
41 | else if (x < xm1 && (y > ym1 && y < ym2)) | ||
42 | return RGB(0, 0, 255); | ||
43 | // blue bar on the top | ||
44 | else if (y < ym1 && (x > xm1 && x < xm2)) | ||
45 | return RGB(0, 0, 255); | ||
46 | // red bar on the right | ||
47 | else if (x > xm2 && (y > ym1 && y < ym2)) | ||
48 | return RGB(255, 0, 0); | ||
49 | // red bar on the bottom | ||
50 | else if (y > ym2 && (x > xm1 && x < xm2)) | ||
51 | return RGB(255, 0, 0); | ||
52 | // inside the margins | ||
53 | else if (x > xm1 && x < xm2 && y > ym1 && y < ym2) { | ||
54 | // diagonal line | ||
55 | if (x == y || w - x == h - y) | ||
56 | return RGB(255, 255, 255); | ||
57 | // diagonal line | ||
58 | else if (w - x - 1 == y || x == h - y - 1) | ||
59 | return RGB(255, 255, 255); | ||
60 | else { | ||
61 | int t = (x - xm1 - 1) * 8 / (xm2 - xm1 - 1); | ||
62 | unsigned r = 0, g = 0, b = 0; | ||
63 | |||
64 | unsigned c = (y - ym1 - 1) % 256; | ||
65 | |||
66 | switch (t) { | ||
67 | case 0: | ||
68 | r = c; | ||
69 | break; | ||
70 | case 1: | ||
71 | g = c; | ||
72 | break; | ||
73 | case 2: | ||
74 | b = c; | ||
75 | break; | ||
76 | case 3: | ||
77 | g = b = c; | ||
78 | break; | ||
79 | case 4: | ||
80 | r = b = c; | ||
81 | break; | ||
82 | case 5: | ||
83 | r = g = c; | ||
84 | break; | ||
85 | case 6: | ||
86 | r = g = b = c; | ||
87 | break; | ||
88 | case 7: | ||
89 | break; | ||
90 | } | ||
91 | |||
92 | return RGB(r, g, b); | ||
93 | } | ||
94 | } else { | ||
95 | // black corners | ||
96 | return RGB(0, 0, 0); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static void draw_test_pattern_part(IFramebuffer& fb, unsigned start_y, unsigned end_y, YUVType yuvt) | ||
101 | { | ||
102 | unsigned x, y; | ||
103 | unsigned w = fb.width(); | ||
104 | |||
105 | switch (fb.format()) { | ||
106 | case PixelFormat::XRGB8888: | ||
107 | case PixelFormat::XBGR8888: | ||
108 | case PixelFormat::ARGB8888: | ||
109 | case PixelFormat::ABGR8888: | ||
110 | case PixelFormat::RGB888: | ||
111 | case PixelFormat::BGR888: | ||
112 | case PixelFormat::RGB565: | ||
113 | case PixelFormat::BGR565: | ||
114 | for (y = start_y; y < end_y; y++) { | ||
115 | for (x = 0; x < w; x++) { | ||
116 | RGB pixel = get_test_pattern_pixel(fb, x, y); | ||
117 | draw_rgb_pixel(fb, x, y, pixel); | ||
118 | } | ||
119 | } | ||
120 | break; | ||
121 | |||
122 | case PixelFormat::UYVY: | ||
123 | case PixelFormat::YUYV: | ||
124 | case PixelFormat::YVYU: | ||
125 | case PixelFormat::VYUY: | ||
126 | for (y = start_y; y < end_y; y++) { | ||
127 | for (x = 0; x < w; x += 2) { | ||
128 | RGB pixel1 = get_test_pattern_pixel(fb, x, y); | ||
129 | RGB pixel2 = get_test_pattern_pixel(fb, x + 1, y); | ||
130 | draw_yuv422_macropixel(fb, x, y, pixel1.yuv(yuvt), pixel2.yuv(yuvt)); | ||
131 | } | ||
132 | } | ||
133 | break; | ||
134 | |||
135 | case PixelFormat::NV12: | ||
136 | case PixelFormat::NV21: | ||
137 | for (y = start_y; y < end_y; y += 2) { | ||
138 | for (x = 0; x < w; x += 2) { | ||
139 | RGB pixel00 = get_test_pattern_pixel(fb, x, y); | ||
140 | RGB pixel10 = get_test_pattern_pixel(fb, x + 1, y); | ||
141 | RGB pixel01 = get_test_pattern_pixel(fb, x, y + 1); | ||
142 | RGB pixel11 = get_test_pattern_pixel(fb, x + 1, y + 1); | ||
143 | draw_yuv420_macropixel(fb, x, y, | ||
144 | pixel00.yuv(yuvt), pixel10.yuv(yuvt), | ||
145 | pixel01.yuv(yuvt), pixel11.yuv(yuvt)); | ||
146 | } | ||
147 | } | ||
148 | break; | ||
149 | default: | ||
150 | throw std::invalid_argument("unknown pixelformat"); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void draw_test_pattern_impl(IFramebuffer& fb, YUVType yuvt) | ||
155 | { | ||
156 | if (fb.height() < 20) { | ||
157 | draw_test_pattern_part(fb, 0, fb.height(), yuvt); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | // Create the mmaps before starting the threads | ||
162 | for (unsigned i = 0; i < fb.num_planes(); ++i) | ||
163 | fb.map(0); | ||
164 | |||
165 | unsigned num_threads = thread::hardware_concurrency(); | ||
166 | vector<thread> workers; | ||
167 | |||
168 | unsigned part = (fb.height() / num_threads) & ~1; | ||
169 | |||
170 | for (unsigned n = 0; n < num_threads; ++n) { | ||
171 | unsigned start = n * part; | ||
172 | unsigned end = start + part; | ||
173 | |||
174 | if (n == num_threads - 1) | ||
175 | end = fb.height(); | ||
176 | |||
177 | workers.push_back(thread([&fb, start, end, yuvt]() { draw_test_pattern_part(fb, start, end, yuvt); })); | ||
178 | } | ||
179 | |||
180 | for (thread& t : workers) | ||
181 | t.join(); | ||
182 | } | ||
183 | |||
184 | void draw_test_pattern(IFramebuffer &fb, YUVType yuvt) | ||
185 | { | ||
186 | #ifdef DRAW_PERF_PRINT | ||
187 | Stopwatch sw; | ||
188 | sw.start(); | ||
189 | #endif | ||
190 | |||
191 | draw_test_pattern_impl(fb, yuvt); | ||
192 | |||
193 | #ifdef DRAW_PERF_PRINT | ||
194 | double us = sw.elapsed_us(); | ||
195 | printf("draw took %u us\n", (unsigned)us); | ||
196 | #endif | ||
197 | } | ||
198 | |||
199 | } | ||
diff --git a/kms++util/src/videodevice.cpp b/kms++util/src/videodevice.cpp new file mode 100644 index 0000000..efe1678 --- /dev/null +++ b/kms++util/src/videodevice.cpp | |||
@@ -0,0 +1,461 @@ | |||
1 | #include <string> | ||
2 | |||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <linux/videodev2.h> | ||
7 | #include <sys/ioctl.h> | ||
8 | #include <unistd.h> | ||
9 | #include <system_error> | ||
10 | |||
11 | #include <kms++/kms++.h> | ||
12 | #include <kms++util/kms++util.h> | ||
13 | #include <kms++util/videodevice.h> | ||
14 | |||
15 | using namespace std; | ||
16 | using namespace kms; | ||
17 | |||
18 | /* V4L2 helper funcs */ | ||
19 | static vector<PixelFormat> v4l2_get_formats(int fd, uint32_t buf_type) | ||
20 | { | ||
21 | vector<PixelFormat> v; | ||
22 | |||
23 | v4l2_fmtdesc desc { }; | ||
24 | desc.type = buf_type; | ||
25 | |||
26 | while (ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) { | ||
27 | v.push_back((PixelFormat)desc.pixelformat); | ||
28 | desc.index++; | ||
29 | } | ||
30 | |||
31 | return v; | ||
32 | } | ||
33 | |||
34 | static void v4l2_set_format(int fd, PixelFormat fmt, uint32_t width, uint32_t height, uint32_t buf_type) | ||
35 | { | ||
36 | int r; | ||
37 | |||
38 | v4l2_format v4lfmt { }; | ||
39 | |||
40 | v4lfmt.type = buf_type; | ||
41 | r = ioctl(fd, VIDIOC_G_FMT, &v4lfmt); | ||
42 | ASSERT(r == 0); | ||
43 | |||
44 | const PixelFormatInfo& pfi = get_pixel_format_info(fmt); | ||
45 | |||
46 | bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
47 | |||
48 | if (mplane) { | ||
49 | v4l2_pix_format_mplane& mp = v4lfmt.fmt.pix_mp; | ||
50 | |||
51 | mp.pixelformat = (uint32_t)fmt; | ||
52 | mp.width = width; | ||
53 | mp.height = height; | ||
54 | |||
55 | mp.num_planes = pfi.num_planes; | ||
56 | |||
57 | for (unsigned i = 0; i < pfi.num_planes; ++i) { | ||
58 | const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; | ||
59 | v4l2_plane_pix_format& p = mp.plane_fmt[i]; | ||
60 | |||
61 | p.bytesperline = width * pfpi.bitspp / 8; | ||
62 | p.sizeimage = p.bytesperline * height / pfpi.ysub; | ||
63 | } | ||
64 | |||
65 | r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); | ||
66 | ASSERT(r == 0); | ||
67 | |||
68 | ASSERT(mp.pixelformat == (uint32_t)fmt); | ||
69 | ASSERT(mp.width == width); | ||
70 | ASSERT(mp.height == height); | ||
71 | |||
72 | ASSERT(mp.num_planes == pfi.num_planes); | ||
73 | |||
74 | for (unsigned i = 0; i < pfi.num_planes; ++i) { | ||
75 | const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; | ||
76 | v4l2_plane_pix_format& p = mp.plane_fmt[i]; | ||
77 | |||
78 | ASSERT(p.bytesperline == width * pfpi.bitspp / 8); | ||
79 | ASSERT(p.sizeimage == p.bytesperline * height / pfpi.ysub); | ||
80 | } | ||
81 | } else { | ||
82 | ASSERT(pfi.num_planes == 1); | ||
83 | |||
84 | v4lfmt.fmt.pix.pixelformat = (uint32_t)fmt; | ||
85 | v4lfmt.fmt.pix.width = width; | ||
86 | v4lfmt.fmt.pix.height = height; | ||
87 | v4lfmt.fmt.pix.bytesperline = width * pfi.planes[0].bitspp / 8; | ||
88 | |||
89 | r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); | ||
90 | ASSERT(r == 0); | ||
91 | |||
92 | ASSERT(v4lfmt.fmt.pix.pixelformat == (uint32_t)fmt); | ||
93 | ASSERT(v4lfmt.fmt.pix.width == width); | ||
94 | ASSERT(v4lfmt.fmt.pix.height == height); | ||
95 | ASSERT(v4lfmt.fmt.pix.bytesperline == width * pfi.planes[0].bitspp / 8); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static void v4l2_request_bufs(int fd, uint32_t queue_size, uint32_t buf_type) | ||
100 | { | ||
101 | v4l2_requestbuffers v4lreqbuf { }; | ||
102 | v4lreqbuf.type = buf_type; | ||
103 | v4lreqbuf.memory = V4L2_MEMORY_DMABUF; | ||
104 | v4lreqbuf.count = queue_size; | ||
105 | int r = ioctl(fd, VIDIOC_REQBUFS, &v4lreqbuf); | ||
106 | ASSERT(r == 0); | ||
107 | ASSERT(v4lreqbuf.count == queue_size); | ||
108 | } | ||
109 | |||
110 | static void v4l2_queue_dmabuf(int fd, uint32_t index, DumbFramebuffer* fb, uint32_t buf_type) | ||
111 | { | ||
112 | v4l2_buffer buf { }; | ||
113 | buf.type = buf_type; | ||
114 | buf.memory = V4L2_MEMORY_DMABUF; | ||
115 | buf.index = index; | ||
116 | |||
117 | const PixelFormatInfo& pfi = get_pixel_format_info(fb->format()); | ||
118 | |||
119 | bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
120 | |||
121 | if (mplane) { | ||
122 | buf.length = pfi.num_planes; | ||
123 | |||
124 | v4l2_plane planes[4] { }; | ||
125 | buf.m.planes = planes; | ||
126 | |||
127 | for (unsigned i = 0; i < pfi.num_planes; ++i) { | ||
128 | planes[i].m.fd = fb->prime_fd(i); | ||
129 | planes[i].bytesused = fb->size(i); | ||
130 | planes[i].length = fb->size(i); | ||
131 | } | ||
132 | |||
133 | int r = ioctl(fd, VIDIOC_QBUF, &buf); | ||
134 | ASSERT(r == 0); | ||
135 | } else { | ||
136 | buf.m.fd = fb->prime_fd(0); | ||
137 | |||
138 | int r = ioctl(fd, VIDIOC_QBUF, &buf); | ||
139 | ASSERT(r == 0); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static uint32_t v4l2_dequeue(int fd, uint32_t buf_type) | ||
144 | { | ||
145 | v4l2_buffer buf { }; | ||
146 | buf.type = buf_type; | ||
147 | buf.memory = V4L2_MEMORY_DMABUF; | ||
148 | |||
149 | // V4L2 crashes if planes are not set | ||
150 | v4l2_plane planes[4] { }; | ||
151 | buf.m.planes = planes; | ||
152 | buf.length = 4; | ||
153 | |||
154 | int r = ioctl(fd, VIDIOC_DQBUF, &buf); | ||
155 | if (r) | ||
156 | throw system_error(errno, generic_category()); | ||
157 | |||
158 | return buf.index; | ||
159 | } | ||
160 | |||
161 | |||
162 | |||
163 | |||
164 | VideoDevice::VideoDevice(const string& dev) | ||
165 | :VideoDevice(::open(dev.c_str(), O_RDWR | O_NONBLOCK)) | ||
166 | { | ||
167 | } | ||
168 | |||
169 | VideoDevice::VideoDevice(int fd) | ||
170 | : m_fd(fd), m_has_capture(false), m_has_output(false), m_has_m2m(false), m_capture_streamer(0), m_output_streamer(0) | ||
171 | { | ||
172 | FAIL_IF(fd < 0, "Bad fd"); | ||
173 | |||
174 | struct v4l2_capability cap = { }; | ||
175 | int r = ioctl(fd, VIDIOC_QUERYCAP, &cap); | ||
176 | ASSERT(r == 0); | ||
177 | |||
178 | if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) { | ||
179 | m_has_capture = true; | ||
180 | m_has_mplane_capture = true; | ||
181 | } else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { | ||
182 | m_has_capture = true; | ||
183 | m_has_mplane_capture = false; | ||
184 | } | ||
185 | |||
186 | if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) { | ||
187 | m_has_output = true; | ||
188 | m_has_mplane_output = true; | ||
189 | } else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) { | ||
190 | m_has_output = true; | ||
191 | m_has_mplane_output = false; | ||
192 | } | ||
193 | |||
194 | if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { | ||
195 | m_has_m2m = true; | ||
196 | m_has_capture = true; | ||
197 | m_has_output = true; | ||
198 | m_has_mplane_m2m = true; | ||
199 | m_has_mplane_capture = true; | ||
200 | m_has_mplane_output = true; | ||
201 | } else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { | ||
202 | m_has_m2m = true; | ||
203 | m_has_capture = true; | ||
204 | m_has_output = true; | ||
205 | m_has_mplane_m2m = false; | ||
206 | m_has_mplane_capture = false; | ||
207 | m_has_mplane_output = false; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | VideoDevice::~VideoDevice() | ||
212 | { | ||
213 | ::close(m_fd); | ||
214 | } | ||
215 | |||
216 | VideoStreamer* VideoDevice::get_capture_streamer() | ||
217 | { | ||
218 | ASSERT(m_has_capture); | ||
219 | |||
220 | if (!m_capture_streamer) { | ||
221 | auto type = m_has_mplane_capture ? VideoStreamer::StreamerType::CaptureMulti : VideoStreamer::StreamerType::CaptureSingle; | ||
222 | m_capture_streamer = new VideoStreamer(m_fd, type); | ||
223 | } | ||
224 | |||
225 | return m_capture_streamer; | ||
226 | } | ||
227 | |||
228 | VideoStreamer* VideoDevice::get_output_streamer() | ||
229 | { | ||
230 | ASSERT(m_has_output); | ||
231 | |||
232 | if (!m_output_streamer) { | ||
233 | auto type = m_has_mplane_output ? VideoStreamer::StreamerType::OutputMulti : VideoStreamer::StreamerType::OutputSingle; | ||
234 | m_output_streamer = new VideoStreamer(m_fd, type); | ||
235 | } | ||
236 | |||
237 | return m_output_streamer; | ||
238 | } | ||
239 | |||
240 | vector<tuple<uint32_t, uint32_t>> VideoDevice::get_discrete_frame_sizes(PixelFormat fmt) | ||
241 | { | ||
242 | vector<tuple<uint32_t, uint32_t>> v; | ||
243 | |||
244 | v4l2_frmsizeenum v4lfrms { }; | ||
245 | v4lfrms.pixel_format = (uint32_t)fmt; | ||
246 | |||
247 | int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); | ||
248 | ASSERT(r); | ||
249 | |||
250 | FAIL_IF(v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE, "No discrete frame sizes"); | ||
251 | |||
252 | while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) { | ||
253 | v.emplace_back(v4lfrms.discrete.width, v4lfrms.discrete.height); | ||
254 | v4lfrms.index++; | ||
255 | }; | ||
256 | |||
257 | return v; | ||
258 | } | ||
259 | |||
260 | VideoDevice::VideoFrameSize VideoDevice::get_frame_sizes(PixelFormat fmt) | ||
261 | { | ||
262 | v4l2_frmsizeenum v4lfrms { }; | ||
263 | v4lfrms.pixel_format = (uint32_t)fmt; | ||
264 | |||
265 | int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); | ||
266 | ASSERT(r); | ||
267 | |||
268 | FAIL_IF(v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE, "No continuous frame sizes"); | ||
269 | |||
270 | VideoFrameSize s; | ||
271 | |||
272 | s.min_w = v4lfrms.stepwise.min_width; | ||
273 | s.max_w = v4lfrms.stepwise.max_width; | ||
274 | s.step_w = v4lfrms.stepwise.step_width; | ||
275 | |||
276 | s.min_h = v4lfrms.stepwise.min_height; | ||
277 | s.max_h = v4lfrms.stepwise.max_height; | ||
278 | s.step_h = v4lfrms.stepwise.step_height; | ||
279 | |||
280 | return s; | ||
281 | } | ||
282 | |||
283 | vector<string> VideoDevice::get_capture_devices() | ||
284 | { | ||
285 | vector<string> v; | ||
286 | |||
287 | for (int i = 0; i < 20; ++i) { | ||
288 | string name = "/dev/video" + to_string(i); | ||
289 | |||
290 | struct stat buffer; | ||
291 | if (stat(name.c_str(), &buffer) != 0) | ||
292 | continue; | ||
293 | |||
294 | VideoDevice vid(name); | ||
295 | |||
296 | if (vid.has_capture() && !vid.has_m2m()) | ||
297 | v.push_back(name); | ||
298 | } | ||
299 | |||
300 | return v; | ||
301 | } | ||
302 | |||
303 | vector<string> VideoDevice::get_m2m_devices() | ||
304 | { | ||
305 | vector<string> v; | ||
306 | |||
307 | for (int i = 0; i < 20; ++i) { | ||
308 | string name = "/dev/video" + to_string(i); | ||
309 | |||
310 | struct stat buffer; | ||
311 | if (stat(name.c_str(), &buffer) != 0) | ||
312 | continue; | ||
313 | |||
314 | VideoDevice vid(name); | ||
315 | |||
316 | if (vid.has_m2m()) | ||
317 | v.push_back(name); | ||
318 | } | ||
319 | |||
320 | return v; | ||
321 | } | ||
322 | |||
323 | |||
324 | VideoStreamer::VideoStreamer(int fd, StreamerType type) | ||
325 | : m_fd(fd), m_type(type) | ||
326 | { | ||
327 | |||
328 | } | ||
329 | |||
330 | std::vector<string> VideoStreamer::get_ports() | ||
331 | { | ||
332 | vector<string> v; | ||
333 | |||
334 | switch (m_type) { | ||
335 | case StreamerType::CaptureSingle: | ||
336 | case StreamerType::CaptureMulti: | ||
337 | { | ||
338 | struct v4l2_input input { }; | ||
339 | |||
340 | while (ioctl(m_fd, VIDIOC_ENUMINPUT, &input) == 0) { | ||
341 | v.push_back(string((char*)&input.name)); | ||
342 | input.index++; | ||
343 | } | ||
344 | |||
345 | break; | ||
346 | } | ||
347 | |||
348 | case StreamerType::OutputSingle: | ||
349 | case StreamerType::OutputMulti: | ||
350 | { | ||
351 | struct v4l2_output output { }; | ||
352 | |||
353 | while (ioctl(m_fd, VIDIOC_ENUMOUTPUT, &output) == 0) { | ||
354 | v.push_back(string((char*)&output.name)); | ||
355 | output.index++; | ||
356 | } | ||
357 | |||
358 | break; | ||
359 | } | ||
360 | |||
361 | default: | ||
362 | FAIL("Bad StreamerType"); | ||
363 | } | ||
364 | |||
365 | return v; | ||
366 | } | ||
367 | |||
368 | void VideoStreamer::set_port(uint32_t index) | ||
369 | { | ||
370 | unsigned long req; | ||
371 | |||
372 | switch (m_type) { | ||
373 | case StreamerType::CaptureSingle: | ||
374 | case StreamerType::CaptureMulti: | ||
375 | req = VIDIOC_S_INPUT; | ||
376 | break; | ||
377 | |||
378 | case StreamerType::OutputSingle: | ||
379 | case StreamerType::OutputMulti: | ||
380 | req = VIDIOC_S_OUTPUT; | ||
381 | break; | ||
382 | |||
383 | default: | ||
384 | FAIL("Bad StreamerType"); | ||
385 | } | ||
386 | |||
387 | int r = ioctl(m_fd, req, &index); | ||
388 | ASSERT(r == 0); | ||
389 | } | ||
390 | |||
391 | static v4l2_buf_type get_buf_type(VideoStreamer::StreamerType type) | ||
392 | { | ||
393 | switch (type) { | ||
394 | case VideoStreamer::StreamerType::CaptureSingle: | ||
395 | return V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
396 | case VideoStreamer::StreamerType::CaptureMulti: | ||
397 | return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | ||
398 | case VideoStreamer::StreamerType::OutputSingle: | ||
399 | return V4L2_BUF_TYPE_VIDEO_OUTPUT; | ||
400 | case VideoStreamer::StreamerType::OutputMulti: | ||
401 | return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | ||
402 | default: | ||
403 | FAIL("Bad StreamerType"); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | std::vector<PixelFormat> VideoStreamer::get_formats() | ||
408 | { | ||
409 | return v4l2_get_formats(m_fd, get_buf_type(m_type)); | ||
410 | } | ||
411 | |||
412 | void VideoStreamer::set_format(PixelFormat fmt, uint32_t width, uint32_t height) | ||
413 | { | ||
414 | v4l2_set_format(m_fd, fmt, width, height, get_buf_type(m_type)); | ||
415 | } | ||
416 | |||
417 | void VideoStreamer::set_queue_size(uint32_t queue_size) | ||
418 | { | ||
419 | v4l2_request_bufs(m_fd, queue_size, get_buf_type(m_type)); | ||
420 | m_fbs.resize(queue_size); | ||
421 | } | ||
422 | |||
423 | void VideoStreamer::queue(DumbFramebuffer* fb) | ||
424 | { | ||
425 | uint32_t idx; | ||
426 | |||
427 | for (idx = 0; idx < m_fbs.size(); ++idx) { | ||
428 | if (m_fbs[idx] == nullptr) | ||
429 | break; | ||
430 | } | ||
431 | |||
432 | FAIL_IF(idx == m_fbs.size(), "queue full"); | ||
433 | |||
434 | m_fbs[idx] = fb; | ||
435 | |||
436 | v4l2_queue_dmabuf(m_fd, idx, fb, get_buf_type(m_type)); | ||
437 | } | ||
438 | |||
439 | DumbFramebuffer* VideoStreamer::dequeue() | ||
440 | { | ||
441 | uint32_t idx = v4l2_dequeue(m_fd, get_buf_type(m_type)); | ||
442 | |||
443 | auto fb = m_fbs[idx]; | ||
444 | m_fbs[idx] = nullptr; | ||
445 | |||
446 | return fb; | ||
447 | } | ||
448 | |||
449 | void VideoStreamer::stream_on() | ||
450 | { | ||
451 | uint32_t buf_type = get_buf_type(m_type); | ||
452 | int r = ioctl(m_fd, VIDIOC_STREAMON, &buf_type); | ||
453 | FAIL_IF(r, "Failed to enable stream: %d", r); | ||
454 | } | ||
455 | |||
456 | void VideoStreamer::stream_off() | ||
457 | { | ||
458 | uint32_t buf_type = get_buf_type(m_type); | ||
459 | int r = ioctl(m_fd, VIDIOC_STREAMOFF, &buf_type); | ||
460 | FAIL_IF(r, "Failed to disable stream: %d", r); | ||
461 | } | ||
diff --git a/kmscube/CMakeLists.txt b/kmscube/CMakeLists.txt new file mode 100644 index 0000000..b6bac29 --- /dev/null +++ b/kmscube/CMakeLists.txt | |||
@@ -0,0 +1,45 @@ | |||
1 | |||
2 | pkg_check_modules(GLESv2 glesv2 REQUIRED) | ||
3 | pkg_check_modules(EGL egl REQUIRED) | ||
4 | pkg_check_modules(GBM gbm REQUIRED) | ||
5 | pkg_check_modules(X11 x11 REQUIRED) | ||
6 | pkg_check_modules(XCB xcb REQUIRED) | ||
7 | pkg_check_modules(X11XCB x11-xcb REQUIRED) | ||
8 | pkg_check_modules(WL wayland-client REQUIRED) | ||
9 | pkg_check_modules(WL_EGL wayland-egl REQUIRED) | ||
10 | |||
11 | |||
12 | include_directories( | ||
13 | ${LIBDRM_INCLUDE_DIRS} | ||
14 | ${GLESv2_INCLUDE_DIRS} | ||
15 | ${EGL_INCLUDE_DIRS} | ||
16 | ${GBM_INCLUDE_DIRS} | ||
17 | ${X11_INCLUDE_DIRS} | ||
18 | ${XCB_INCLUDE_DIRS} | ||
19 | ${X11XCB_INCLUDE_DIRS} | ||
20 | ) | ||
21 | |||
22 | link_directories( | ||
23 | ${LIBDRM_LIBRARY_DIRS} | ||
24 | ${GLESv2_LIBRARY_DIRS} | ||
25 | ${EGL_LIBRARY_DIRS} | ||
26 | ${GBM_LIBRARY_DIRS} | ||
27 | ${X11_LIBRARY_DIRS} | ||
28 | ${XCB_LIBRARY_DIRS} | ||
29 | ${X11XCB_LIBRARY_DIRS} | ||
30 | ) | ||
31 | |||
32 | add_executable (kmscube cube.cpp cube.h cube-egl.cpp cube-egl.h cube-gles2.cpp cube-gles2.h | ||
33 | cube-null.cpp cube-gbm.cpp cube-x11.cpp cube-wl.cpp | ||
34 | esTransform.c esTransform.h) | ||
35 | target_link_libraries(kmscube kms++ kms++util | ||
36 | ${LIBDRM_LIBRARIES} | ||
37 | ${GLESv2_LIBRARIES} | ||
38 | ${EGL_LIBRARIES} | ||
39 | ${GBM_LIBRARIES} | ||
40 | ${X11_LIBRARIES} | ||
41 | ${XCB_LIBRARIES} | ||
42 | ${X11XCB_LIBRARIES} | ||
43 | ${WL_LIBRARIES} | ||
44 | ${WL_EGL_LIBRARIES} | ||
45 | ) | ||
diff --git a/kmscube/cube-egl.cpp b/kmscube/cube-egl.cpp new file mode 100644 index 0000000..81b3107 --- /dev/null +++ b/kmscube/cube-egl.cpp | |||
@@ -0,0 +1,121 @@ | |||
1 | #include "cube-egl.h" | ||
2 | #include "cube.h" | ||
3 | |||
4 | #include <kms++util/kms++util.h> | ||
5 | |||
6 | using namespace std; | ||
7 | |||
8 | static void print_egl_config(EGLDisplay dpy, EGLConfig cfg) | ||
9 | { | ||
10 | auto getconf = [dpy, cfg](EGLint a) { EGLint v = -1; eglGetConfigAttrib(dpy, cfg, a, &v); return v; }; | ||
11 | |||
12 | printf("EGL Config %d: color buf %d/%d/%d/%d = %d, depth %d, stencil %d, native visualid %d, native visualtype %d\n", | ||
13 | getconf(EGL_CONFIG_ID), | ||
14 | getconf(EGL_ALPHA_SIZE), | ||
15 | getconf(EGL_RED_SIZE), | ||
16 | getconf(EGL_GREEN_SIZE), | ||
17 | getconf(EGL_BLUE_SIZE), | ||
18 | getconf(EGL_BUFFER_SIZE), | ||
19 | getconf(EGL_DEPTH_SIZE), | ||
20 | getconf(EGL_STENCIL_SIZE), | ||
21 | getconf(EGL_NATIVE_VISUAL_ID), | ||
22 | getconf(EGL_NATIVE_VISUAL_TYPE)); | ||
23 | } | ||
24 | |||
25 | EglState::EglState(void *native_display) | ||
26 | { | ||
27 | EGLBoolean b; | ||
28 | EGLint major, minor, n; | ||
29 | |||
30 | static const EGLint context_attribs[] = { | ||
31 | EGL_CONTEXT_CLIENT_VERSION, 2, | ||
32 | EGL_NONE | ||
33 | }; | ||
34 | |||
35 | static const EGLint config_attribs[] = { | ||
36 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | ||
37 | EGL_RED_SIZE, 8, | ||
38 | EGL_GREEN_SIZE, 8, | ||
39 | EGL_BLUE_SIZE, 8, | ||
40 | EGL_ALPHA_SIZE, 0, | ||
41 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | ||
42 | EGL_NONE | ||
43 | }; | ||
44 | |||
45 | m_display = eglGetDisplay((EGLNativeDisplayType)native_display); | ||
46 | FAIL_IF(!m_display, "failed to get egl display"); | ||
47 | |||
48 | b = eglInitialize(m_display, &major, &minor); | ||
49 | FAIL_IF(!b, "failed to initialize"); | ||
50 | |||
51 | if (s_verbose) { | ||
52 | printf("Using display %p with EGL version %d.%d\n", m_display, major, minor); | ||
53 | |||
54 | printf("EGL_VENDOR: %s\n", eglQueryString(m_display, EGL_VENDOR)); | ||
55 | printf("EGL_VERSION: %s\n", eglQueryString(m_display, EGL_VERSION)); | ||
56 | printf("EGL_EXTENSIONS: %s\n", eglQueryString(m_display, EGL_EXTENSIONS)); | ||
57 | printf("EGL_CLIENT_APIS: %s\n", eglQueryString(m_display, EGL_CLIENT_APIS)); | ||
58 | } | ||
59 | |||
60 | b = eglBindAPI(EGL_OPENGL_ES_API); | ||
61 | FAIL_IF(!b, "failed to bind api EGL_OPENGL_ES_API"); | ||
62 | |||
63 | |||
64 | if (s_verbose) { | ||
65 | EGLint numConfigs; | ||
66 | b = eglGetConfigs(m_display, nullptr, 0, &numConfigs); | ||
67 | FAIL_IF(!b, "failed to get number of configs"); | ||
68 | |||
69 | EGLConfig configs[numConfigs]; | ||
70 | b = eglGetConfigs(m_display, configs, numConfigs, &numConfigs); | ||
71 | |||
72 | printf("Available configs:\n"); | ||
73 | |||
74 | for (int i = 0; i < numConfigs; ++i) | ||
75 | print_egl_config(m_display, configs[i]); | ||
76 | } | ||
77 | |||
78 | b = eglChooseConfig(m_display, config_attribs, &m_config, 1, &n); | ||
79 | FAIL_IF(!b || n != 1, "failed to choose config"); | ||
80 | |||
81 | if (s_verbose) { | ||
82 | printf("Chosen config:\n"); | ||
83 | print_egl_config(m_display, m_config); | ||
84 | } | ||
85 | |||
86 | m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs); | ||
87 | FAIL_IF(!m_context, "failed to create context"); | ||
88 | |||
89 | EGLBoolean ok = eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context); | ||
90 | FAIL_IF(!ok, "eglMakeCurrent() failed"); | ||
91 | } | ||
92 | |||
93 | EglState::~EglState() | ||
94 | { | ||
95 | eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | ||
96 | eglTerminate(m_display); | ||
97 | } | ||
98 | |||
99 | EglSurface::EglSurface(const EglState &egl, void *native_window) | ||
100 | : egl(egl) | ||
101 | { | ||
102 | esurface = eglCreateWindowSurface(egl.display(), egl.config(), (EGLNativeWindowType)native_window, NULL); | ||
103 | FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface"); | ||
104 | } | ||
105 | |||
106 | EglSurface::~EglSurface() | ||
107 | { | ||
108 | eglDestroySurface(egl.display(), esurface); | ||
109 | } | ||
110 | |||
111 | void EglSurface::make_current() | ||
112 | { | ||
113 | EGLBoolean ok = eglMakeCurrent(egl.display(), esurface, esurface, egl.context()); | ||
114 | FAIL_IF(!ok, "eglMakeCurrent() failed"); | ||
115 | } | ||
116 | |||
117 | void EglSurface::swap_buffers() | ||
118 | { | ||
119 | EGLBoolean ok = eglSwapBuffers(egl.display(), esurface); | ||
120 | FAIL_IF(!ok, "eglMakeCurrent() failed"); | ||
121 | } | ||
diff --git a/kmscube/cube-egl.h b/kmscube/cube-egl.h new file mode 100644 index 0000000..a7de103 --- /dev/null +++ b/kmscube/cube-egl.h | |||
@@ -0,0 +1,34 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <EGL/egl.h> | ||
4 | |||
5 | class EglState | ||
6 | { | ||
7 | public: | ||
8 | EglState(void *native_display); | ||
9 | ~EglState(); | ||
10 | |||
11 | EGLDisplay display() const { return m_display; } | ||
12 | EGLConfig config() const { return m_config; } | ||
13 | EGLContext context() const { return m_context; } | ||
14 | |||
15 | private: | ||
16 | EGLDisplay m_display; | ||
17 | EGLConfig m_config; | ||
18 | EGLContext m_context; | ||
19 | }; | ||
20 | |||
21 | class EglSurface | ||
22 | { | ||
23 | public: | ||
24 | EglSurface(const EglState& egl, void *native_window); | ||
25 | ~EglSurface(); | ||
26 | |||
27 | void make_current(); | ||
28 | void swap_buffers(); | ||
29 | |||
30 | private: | ||
31 | const EglState& egl; | ||
32 | |||
33 | EGLSurface esurface; | ||
34 | }; | ||
diff --git a/kmscube/cube-gbm.cpp b/kmscube/cube-gbm.cpp new file mode 100644 index 0000000..e239a0c --- /dev/null +++ b/kmscube/cube-gbm.cpp | |||
@@ -0,0 +1,370 @@ | |||
1 | #include <chrono> | ||
2 | #include <cstdio> | ||
3 | #include <vector> | ||
4 | #include <memory> | ||
5 | #include <algorithm> | ||
6 | #include <poll.h> | ||
7 | |||
8 | #include <xf86drm.h> | ||
9 | #include <xf86drmMode.h> | ||
10 | #include <gbm.h> | ||
11 | |||
12 | #include <kms++/kms++.h> | ||
13 | #include <kms++util/kms++util.h> | ||
14 | #include "cube-egl.h" | ||
15 | #include "cube-gles2.h" | ||
16 | |||
17 | using namespace kms; | ||
18 | using namespace std; | ||
19 | |||
20 | static int s_flip_pending; | ||
21 | static bool s_need_exit; | ||
22 | |||
23 | static bool s_support_planes; | ||
24 | |||
25 | class GbmDevice | ||
26 | { | ||
27 | public: | ||
28 | GbmDevice(Card& card) | ||
29 | { | ||
30 | m_dev = gbm_create_device(card.fd()); | ||
31 | FAIL_IF(!m_dev, "failed to create gbm device"); | ||
32 | } | ||
33 | |||
34 | ~GbmDevice() | ||
35 | { | ||
36 | gbm_device_destroy(m_dev); | ||
37 | } | ||
38 | |||
39 | GbmDevice(const GbmDevice& other) = delete; | ||
40 | GbmDevice& operator=(const GbmDevice& other) = delete; | ||
41 | |||
42 | struct gbm_device* handle() const { return m_dev; } | ||
43 | |||
44 | private: | ||
45 | struct gbm_device* m_dev; | ||
46 | }; | ||
47 | |||
48 | class GbmSurface | ||
49 | { | ||
50 | public: | ||
51 | GbmSurface(GbmDevice& gdev, int width, int height) | ||
52 | { | ||
53 | m_surface = gbm_surface_create(gdev.handle(), width, height, | ||
54 | GBM_FORMAT_XRGB8888, | ||
55 | GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); | ||
56 | FAIL_IF(!m_surface, "failed to create gbm surface"); | ||
57 | } | ||
58 | |||
59 | ~GbmSurface() | ||
60 | { | ||
61 | gbm_surface_destroy(m_surface); | ||
62 | } | ||
63 | |||
64 | GbmSurface(const GbmSurface& other) = delete; | ||
65 | GbmSurface& operator=(const GbmSurface& other) = delete; | ||
66 | |||
67 | bool has_free() | ||
68 | { | ||
69 | return gbm_surface_has_free_buffers(m_surface); | ||
70 | } | ||
71 | |||
72 | gbm_bo* lock_front_buffer() | ||
73 | { | ||
74 | return gbm_surface_lock_front_buffer(m_surface); | ||
75 | } | ||
76 | |||
77 | void release_buffer(gbm_bo *bo) | ||
78 | { | ||
79 | gbm_surface_release_buffer(m_surface, bo); | ||
80 | } | ||
81 | |||
82 | struct gbm_surface* handle() const { return m_surface; } | ||
83 | |||
84 | private: | ||
85 | struct gbm_surface* m_surface; | ||
86 | }; | ||
87 | |||
88 | class GbmEglSurface | ||
89 | { | ||
90 | public: | ||
91 | GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height) | ||
92 | : card(card), egl(egl), m_width(width), m_height(height), | ||
93 | bo_prev(0), bo_next(0) | ||
94 | { | ||
95 | gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height)); | ||
96 | esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL); | ||
97 | FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface"); | ||
98 | } | ||
99 | |||
100 | ~GbmEglSurface() | ||
101 | { | ||
102 | if (bo_next) | ||
103 | gsurface->release_buffer(bo_next); | ||
104 | eglDestroySurface(egl.display(), esurface); | ||
105 | } | ||
106 | |||
107 | void make_current() | ||
108 | { | ||
109 | FAIL_IF(!gsurface->has_free(), "No free buffers"); | ||
110 | |||
111 | eglMakeCurrent(egl.display(), esurface, esurface, egl.context()); | ||
112 | } | ||
113 | |||
114 | void swap_buffers() | ||
115 | { | ||
116 | eglSwapBuffers(egl.display(), esurface); | ||
117 | } | ||
118 | |||
119 | static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) | ||
120 | { | ||
121 | auto fb = reinterpret_cast<Framebuffer*>(data); | ||
122 | delete fb; | ||
123 | } | ||
124 | |||
125 | static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card) | ||
126 | { | ||
127 | auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo)); | ||
128 | if (fb) | ||
129 | return fb; | ||
130 | |||
131 | uint32_t width = gbm_bo_get_width(bo); | ||
132 | uint32_t height = gbm_bo_get_height(bo); | ||
133 | uint32_t stride = gbm_bo_get_stride(bo); | ||
134 | uint32_t handle = gbm_bo_get_handle(bo).u32; | ||
135 | PixelFormat format = (PixelFormat)gbm_bo_get_format(bo); | ||
136 | |||
137 | vector<uint32_t> handles { handle }; | ||
138 | vector<uint32_t> strides { stride }; | ||
139 | vector<uint32_t> offsets { 0 }; | ||
140 | |||
141 | fb = new ExtFramebuffer(card, width, height, format, handles, strides, offsets); | ||
142 | |||
143 | gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); | ||
144 | |||
145 | return fb; | ||
146 | } | ||
147 | |||
148 | Framebuffer* lock_next() | ||
149 | { | ||
150 | bo_prev = bo_next; | ||
151 | bo_next = gsurface->lock_front_buffer(); | ||
152 | FAIL_IF(!bo_next, "could not lock gbm buffer"); | ||
153 | return drm_fb_get_from_bo(bo_next, card); | ||
154 | } | ||
155 | |||
156 | void free_prev() | ||
157 | { | ||
158 | if (bo_prev) { | ||
159 | gsurface->release_buffer(bo_prev); | ||
160 | bo_prev = 0; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | uint32_t width() const { return m_width; } | ||
165 | uint32_t height() const { return m_height; } | ||
166 | |||
167 | private: | ||
168 | Card& card; | ||
169 | const EglState& egl; | ||
170 | |||
171 | unique_ptr<GbmSurface> gsurface; | ||
172 | EGLSurface esurface; | ||
173 | |||
174 | int m_width; | ||
175 | int m_height; | ||
176 | |||
177 | struct gbm_bo* bo_prev; | ||
178 | struct gbm_bo* bo_next; | ||
179 | }; | ||
180 | |||
181 | class OutputHandler : private PageFlipHandlerBase | ||
182 | { | ||
183 | public: | ||
184 | OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* root_plane, Plane* plane, float rotation_mult) | ||
185 | : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_root_plane(root_plane), m_plane(plane), m_mode(mode), | ||
186 | m_rotation_mult(rotation_mult) | ||
187 | { | ||
188 | m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay)); | ||
189 | m_scene1 = unique_ptr<GlScene>(new GlScene()); | ||
190 | m_scene1->set_viewport(m_surface1->width(), m_surface1->height()); | ||
191 | |||
192 | if (m_plane) { | ||
193 | m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400)); | ||
194 | m_scene2 = unique_ptr<GlScene>(new GlScene()); | ||
195 | m_scene2->set_viewport(m_surface2->width(), m_surface2->height()); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | OutputHandler(const OutputHandler& other) = delete; | ||
200 | OutputHandler& operator=(const OutputHandler& other) = delete; | ||
201 | |||
202 | void setup() | ||
203 | { | ||
204 | int ret; | ||
205 | |||
206 | m_surface1->make_current(); | ||
207 | m_surface1->swap_buffers(); | ||
208 | Framebuffer* fb = m_surface1->lock_next(); | ||
209 | |||
210 | Framebuffer* planefb = 0; | ||
211 | |||
212 | if (m_plane) { | ||
213 | m_surface2->make_current(); | ||
214 | m_surface2->swap_buffers(); | ||
215 | planefb = m_surface2->lock_next(); | ||
216 | } | ||
217 | |||
218 | ret = m_crtc->set_mode(m_connector, *fb, m_mode); | ||
219 | FAIL_IF(ret, "failed to set mode"); | ||
220 | |||
221 | if (m_plane) { | ||
222 | ret = m_crtc->set_plane(m_plane, *planefb, | ||
223 | 0, 0, planefb->width(), planefb->height(), | ||
224 | 0, 0, planefb->width(), planefb->height()); | ||
225 | FAIL_IF(ret, "failed to set plane"); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | void start_flipping() | ||
230 | { | ||
231 | m_t1 = chrono::steady_clock::now(); | ||
232 | queue_next(); | ||
233 | } | ||
234 | |||
235 | private: | ||
236 | void handle_page_flip(uint32_t frame, double time) | ||
237 | { | ||
238 | ++m_frame_num; | ||
239 | |||
240 | if (m_frame_num % 100 == 0) { | ||
241 | auto t2 = chrono::steady_clock::now(); | ||
242 | chrono::duration<float> fsec = t2 - m_t1; | ||
243 | printf("fps: %f\n", 100.0 / fsec.count()); | ||
244 | m_t1 = t2; | ||
245 | } | ||
246 | |||
247 | s_flip_pending--; | ||
248 | |||
249 | m_surface1->free_prev(); | ||
250 | if (m_plane) | ||
251 | m_surface2->free_prev(); | ||
252 | |||
253 | if (s_need_exit) | ||
254 | return; | ||
255 | |||
256 | queue_next(); | ||
257 | } | ||
258 | |||
259 | void queue_next() | ||
260 | { | ||
261 | m_surface1->make_current(); | ||
262 | m_scene1->draw(m_frame_num * m_rotation_mult); | ||
263 | m_surface1->swap_buffers(); | ||
264 | Framebuffer* fb = m_surface1->lock_next(); | ||
265 | |||
266 | Framebuffer* planefb = 0; | ||
267 | |||
268 | if (m_plane) { | ||
269 | m_surface2->make_current(); | ||
270 | m_scene2->draw(m_frame_num * m_rotation_mult * 2); | ||
271 | m_surface2->swap_buffers(); | ||
272 | planefb = m_surface2->lock_next(); | ||
273 | } | ||
274 | |||
275 | int r; | ||
276 | |||
277 | AtomicReq req(m_crtc->card()); | ||
278 | |||
279 | req.add(m_root_plane, "FB_ID", fb->id()); | ||
280 | if (m_plane) | ||
281 | req.add(m_plane, "FB_ID", planefb->id()); | ||
282 | |||
283 | r = req.test(); | ||
284 | FAIL_IF(r, "atomic test failed"); | ||
285 | |||
286 | r = req.commit(this); | ||
287 | FAIL_IF(r, "atomic commit failed"); | ||
288 | |||
289 | s_flip_pending++; | ||
290 | } | ||
291 | |||
292 | int m_frame_num; | ||
293 | chrono::steady_clock::time_point m_t1; | ||
294 | |||
295 | Connector* m_connector; | ||
296 | Crtc* m_crtc; | ||
297 | Plane* m_root_plane; | ||
298 | Plane* m_plane; | ||
299 | Videomode m_mode; | ||
300 | |||
301 | unique_ptr<GbmEglSurface> m_surface1; | ||
302 | unique_ptr<GbmEglSurface> m_surface2; | ||
303 | |||
304 | unique_ptr<GlScene> m_scene1; | ||
305 | unique_ptr<GlScene> m_scene2; | ||
306 | |||
307 | float m_rotation_mult; | ||
308 | }; | ||
309 | |||
310 | void main_gbm() | ||
311 | { | ||
312 | Card card; | ||
313 | |||
314 | FAIL_IF(!card.has_atomic(), "No atomic modesetting"); | ||
315 | |||
316 | GbmDevice gdev(card); | ||
317 | EglState egl(gdev.handle()); | ||
318 | |||
319 | ResourceManager resman(card); | ||
320 | |||
321 | vector<unique_ptr<OutputHandler>> outputs; | ||
322 | |||
323 | float rot_mult = 1; | ||
324 | |||
325 | for (Connector* conn : card.get_connectors()) { | ||
326 | if (!conn->connected()) | ||
327 | continue; | ||
328 | |||
329 | resman.reserve_connector(conn); | ||
330 | |||
331 | Crtc* crtc = resman.reserve_crtc(conn); | ||
332 | auto mode = conn->get_default_mode(); | ||
333 | |||
334 | Plane* root_plane = resman.reserve_generic_plane(crtc); | ||
335 | FAIL_IF(!root_plane, "Root plane not available"); | ||
336 | |||
337 | Plane* plane = nullptr; | ||
338 | |||
339 | if (s_support_planes) | ||
340 | plane = resman.reserve_generic_plane(crtc); | ||
341 | |||
342 | auto out = new OutputHandler(card, gdev, egl, conn, crtc, mode, root_plane, plane, rot_mult); | ||
343 | outputs.emplace_back(out); | ||
344 | |||
345 | rot_mult *= 1.33; | ||
346 | } | ||
347 | |||
348 | for (auto& out : outputs) | ||
349 | out->setup(); | ||
350 | |||
351 | for (auto& out : outputs) | ||
352 | out->start_flipping(); | ||
353 | |||
354 | struct pollfd fds[2] = { }; | ||
355 | fds[0].fd = 0; | ||
356 | fds[0].events = POLLIN; | ||
357 | fds[1].fd = card.fd(); | ||
358 | fds[1].events = POLLIN; | ||
359 | |||
360 | while (!s_need_exit || s_flip_pending) { | ||
361 | int r = poll(fds, ARRAY_SIZE(fds), -1); | ||
362 | FAIL_IF(r < 0, "poll error %d", r); | ||
363 | |||
364 | if (fds[0].revents) | ||
365 | s_need_exit = true; | ||
366 | |||
367 | if (fds[1].revents) | ||
368 | card.call_page_flip_handlers(); | ||
369 | } | ||
370 | } | ||
diff --git a/kmscube/cube-gles2.cpp b/kmscube/cube-gles2.cpp new file mode 100644 index 0000000..05567ea --- /dev/null +++ b/kmscube/cube-gles2.cpp | |||
@@ -0,0 +1,264 @@ | |||
1 | #include "esTransform.h" | ||
2 | #include "cube-gles2.h" | ||
3 | #include "cube.h" | ||
4 | |||
5 | #include <kms++util/kms++util.h> | ||
6 | |||
7 | using namespace std; | ||
8 | |||
9 | GlScene::GlScene() | ||
10 | { | ||
11 | GLuint vertex_shader, fragment_shader; | ||
12 | GLint ret; | ||
13 | |||
14 | static const GLfloat vVertices[] = { | ||
15 | // front | ||
16 | -1.0f, -1.0f, +1.0f, // point blue | ||
17 | +1.0f, -1.0f, +1.0f, // point magenta | ||
18 | -1.0f, +1.0f, +1.0f, // point cyan | ||
19 | +1.0f, +1.0f, +1.0f, // point white | ||
20 | // back | ||
21 | +1.0f, -1.0f, -1.0f, // point red | ||
22 | -1.0f, -1.0f, -1.0f, // point black | ||
23 | +1.0f, +1.0f, -1.0f, // point yellow | ||
24 | -1.0f, +1.0f, -1.0f, // point green | ||
25 | // right | ||
26 | +1.0f, -1.0f, +1.0f, // point magenta | ||
27 | +1.0f, -1.0f, -1.0f, // point red | ||
28 | +1.0f, +1.0f, +1.0f, // point white | ||
29 | +1.0f, +1.0f, -1.0f, // point yellow | ||
30 | // left | ||
31 | -1.0f, -1.0f, -1.0f, // point black | ||
32 | -1.0f, -1.0f, +1.0f, // point blue | ||
33 | -1.0f, +1.0f, -1.0f, // point green | ||
34 | -1.0f, +1.0f, +1.0f, // point cyan | ||
35 | // top | ||
36 | -1.0f, +1.0f, +1.0f, // point cyan | ||
37 | +1.0f, +1.0f, +1.0f, // point white | ||
38 | -1.0f, +1.0f, -1.0f, // point green | ||
39 | +1.0f, +1.0f, -1.0f, // point yellow | ||
40 | // bottom | ||
41 | -1.0f, -1.0f, -1.0f, // point black | ||
42 | +1.0f, -1.0f, -1.0f, // point red | ||
43 | -1.0f, -1.0f, +1.0f, // point blue | ||
44 | +1.0f, -1.0f, +1.0f // point magenta | ||
45 | }; | ||
46 | |||
47 | static const GLfloat vColors[] = { | ||
48 | // front | ||
49 | 0.0f, 0.0f, 1.0f, // blue | ||
50 | 1.0f, 0.0f, 1.0f, // magenta | ||
51 | 0.0f, 1.0f, 1.0f, // cyan | ||
52 | 1.0f, 1.0f, 1.0f, // white | ||
53 | // back | ||
54 | 1.0f, 0.0f, 0.0f, // red | ||
55 | 0.0f, 0.0f, 0.0f, // black | ||
56 | 1.0f, 1.0f, 0.0f, // yellow | ||
57 | 0.0f, 1.0f, 0.0f, // green | ||
58 | // right | ||
59 | 1.0f, 0.0f, 1.0f, // magenta | ||
60 | 1.0f, 0.0f, 0.0f, // red | ||
61 | 1.0f, 1.0f, 1.0f, // white | ||
62 | 1.0f, 1.0f, 0.0f, // yellow | ||
63 | // left | ||
64 | 0.0f, 0.0f, 0.0f, // black | ||
65 | 0.0f, 0.0f, 1.0f, // blue | ||
66 | 0.0f, 1.0f, 0.0f, // green | ||
67 | 0.0f, 1.0f, 1.0f, // cyan | ||
68 | // top | ||
69 | 0.0f, 1.0f, 1.0f, // cyan | ||
70 | 1.0f, 1.0f, 1.0f, // white | ||
71 | 0.0f, 1.0f, 0.0f, // green | ||
72 | 1.0f, 1.0f, 0.0f, // yellow | ||
73 | // bottom | ||
74 | 0.0f, 0.0f, 0.0f, // black | ||
75 | 1.0f, 0.0f, 0.0f, // red | ||
76 | 0.0f, 0.0f, 1.0f, // blue | ||
77 | 1.0f, 0.0f, 1.0f // magenta | ||
78 | }; | ||
79 | |||
80 | static const GLfloat vNormals[] = { | ||
81 | // front | ||
82 | +0.0f, +0.0f, +1.0f, // forward | ||
83 | +0.0f, +0.0f, +1.0f, // forward | ||
84 | +0.0f, +0.0f, +1.0f, // forward | ||
85 | +0.0f, +0.0f, +1.0f, // forward | ||
86 | // back | ||
87 | +0.0f, +0.0f, -1.0f, // backbard | ||
88 | +0.0f, +0.0f, -1.0f, // backbard | ||
89 | +0.0f, +0.0f, -1.0f, // backbard | ||
90 | +0.0f, +0.0f, -1.0f, // backbard | ||
91 | // right | ||
92 | +1.0f, +0.0f, +0.0f, // right | ||
93 | +1.0f, +0.0f, +0.0f, // right | ||
94 | +1.0f, +0.0f, +0.0f, // right | ||
95 | +1.0f, +0.0f, +0.0f, // right | ||
96 | // left | ||
97 | -1.0f, +0.0f, +0.0f, // left | ||
98 | -1.0f, +0.0f, +0.0f, // left | ||
99 | -1.0f, +0.0f, +0.0f, // left | ||
100 | -1.0f, +0.0f, +0.0f, // left | ||
101 | // top | ||
102 | +0.0f, +1.0f, +0.0f, // up | ||
103 | +0.0f, +1.0f, +0.0f, // up | ||
104 | +0.0f, +1.0f, +0.0f, // up | ||
105 | +0.0f, +1.0f, +0.0f, // up | ||
106 | // bottom | ||
107 | +0.0f, -1.0f, +0.0f, // down | ||
108 | +0.0f, -1.0f, +0.0f, // down | ||
109 | +0.0f, -1.0f, +0.0f, // down | ||
110 | +0.0f, -1.0f, +0.0f // down | ||
111 | }; | ||
112 | |||
113 | static const char *vertex_shader_source = | ||
114 | "uniform mat4 modelviewMatrix; \n" | ||
115 | "uniform mat4 modelviewprojectionMatrix;\n" | ||
116 | "uniform mat3 normalMatrix; \n" | ||
117 | " \n" | ||
118 | "attribute vec4 in_position; \n" | ||
119 | "attribute vec3 in_normal; \n" | ||
120 | "attribute vec4 in_color; \n" | ||
121 | "\n" | ||
122 | "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n" | ||
123 | " \n" | ||
124 | "varying vec4 vVaryingColor; \n" | ||
125 | " \n" | ||
126 | "void main() \n" | ||
127 | "{ \n" | ||
128 | " gl_Position = modelviewprojectionMatrix * in_position;\n" | ||
129 | " vec3 vEyeNormal = normalMatrix * in_normal;\n" | ||
130 | " vec4 vPosition4 = modelviewMatrix * in_position;\n" | ||
131 | " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n" | ||
132 | " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n" | ||
133 | " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n" | ||
134 | " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n" | ||
135 | "} \n"; | ||
136 | |||
137 | static const char *fragment_shader_source = | ||
138 | "precision mediump float; \n" | ||
139 | " \n" | ||
140 | "varying vec4 vVaryingColor; \n" | ||
141 | " \n" | ||
142 | "void main() \n" | ||
143 | "{ \n" | ||
144 | " gl_FragColor = vVaryingColor; \n" | ||
145 | "} \n"; | ||
146 | |||
147 | |||
148 | if (s_verbose) { | ||
149 | printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); | ||
150 | printf("GL_VERSION: %s\n", glGetString(GL_VERSION)); | ||
151 | printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); | ||
152 | printf("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); | ||
153 | } | ||
154 | |||
155 | vertex_shader = glCreateShader(GL_VERTEX_SHADER); | ||
156 | |||
157 | glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); | ||
158 | glCompileShader(vertex_shader); | ||
159 | |||
160 | glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret); | ||
161 | FAIL_IF(!ret, "vertex shader compilation failed!"); | ||
162 | |||
163 | fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); | ||
164 | |||
165 | glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); | ||
166 | glCompileShader(fragment_shader); | ||
167 | |||
168 | glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret); | ||
169 | FAIL_IF(!ret, "fragment shader compilation failed!"); | ||
170 | |||
171 | GLuint program = glCreateProgram(); | ||
172 | |||
173 | glAttachShader(program, vertex_shader); | ||
174 | glAttachShader(program, fragment_shader); | ||
175 | |||
176 | glBindAttribLocation(program, 0, "in_position"); | ||
177 | glBindAttribLocation(program, 1, "in_normal"); | ||
178 | glBindAttribLocation(program, 2, "in_color"); | ||
179 | |||
180 | glLinkProgram(program); | ||
181 | |||
182 | glGetProgramiv(program, GL_LINK_STATUS, &ret); | ||
183 | FAIL_IF(!ret, "program linking failed!"); | ||
184 | |||
185 | glUseProgram(program); | ||
186 | |||
187 | m_modelviewmatrix = glGetUniformLocation(program, "modelviewMatrix"); | ||
188 | m_modelviewprojectionmatrix = glGetUniformLocation(program, "modelviewprojectionMatrix"); | ||
189 | m_normalmatrix = glGetUniformLocation(program, "normalMatrix"); | ||
190 | |||
191 | glEnable(GL_CULL_FACE); | ||
192 | |||
193 | GLintptr positionsoffset = 0; | ||
194 | GLintptr colorsoffset = sizeof(vVertices); | ||
195 | GLintptr normalsoffset = sizeof(vVertices) + sizeof(vColors); | ||
196 | GLuint vbo; | ||
197 | |||
198 | glGenBuffers(1, &vbo); | ||
199 | glBindBuffer(GL_ARRAY_BUFFER, vbo); | ||
200 | glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW); | ||
201 | glBufferSubData(GL_ARRAY_BUFFER, positionsoffset, sizeof(vVertices), &vVertices[0]); | ||
202 | glBufferSubData(GL_ARRAY_BUFFER, colorsoffset, sizeof(vColors), &vColors[0]); | ||
203 | glBufferSubData(GL_ARRAY_BUFFER, normalsoffset, sizeof(vNormals), &vNormals[0]); | ||
204 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)positionsoffset); | ||
205 | glEnableVertexAttribArray(0); | ||
206 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)normalsoffset); | ||
207 | glEnableVertexAttribArray(1); | ||
208 | glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)colorsoffset); | ||
209 | glEnableVertexAttribArray(2); | ||
210 | } | ||
211 | |||
212 | void GlScene::set_viewport(uint32_t width, uint32_t height) | ||
213 | { | ||
214 | m_width = width; | ||
215 | m_height = height; | ||
216 | } | ||
217 | |||
218 | void GlScene::draw(uint32_t framenum) | ||
219 | { | ||
220 | glViewport(0, 0, m_width, m_height); | ||
221 | |||
222 | glClearColor(0.5, 0.5, 0.5, 1.0); | ||
223 | glClear(GL_COLOR_BUFFER_BIT); | ||
224 | |||
225 | ESMatrix modelview; | ||
226 | |||
227 | esMatrixLoadIdentity(&modelview); | ||
228 | esTranslate(&modelview, 0.0f, 0.0f, -8.0f); | ||
229 | esRotate(&modelview, 45.0f + (0.75f * framenum), 1.0f, 0.0f, 0.0f); | ||
230 | esRotate(&modelview, 45.0f - (0.5f * framenum), 0.0f, 1.0f, 0.0f); | ||
231 | esRotate(&modelview, 10.0f + (0.45f * framenum), 0.0f, 0.0f, 1.0f); | ||
232 | |||
233 | GLfloat aspect = (float)m_height / m_width; | ||
234 | |||
235 | ESMatrix projection; | ||
236 | esMatrixLoadIdentity(&projection); | ||
237 | esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f); | ||
238 | |||
239 | ESMatrix modelviewprojection; | ||
240 | esMatrixLoadIdentity(&modelviewprojection); | ||
241 | esMatrixMultiply(&modelviewprojection, &modelview, &projection); | ||
242 | |||
243 | float normal[9]; | ||
244 | normal[0] = modelview.m[0][0]; | ||
245 | normal[1] = modelview.m[0][1]; | ||
246 | normal[2] = modelview.m[0][2]; | ||
247 | normal[3] = modelview.m[1][0]; | ||
248 | normal[4] = modelview.m[1][1]; | ||
249 | normal[5] = modelview.m[1][2]; | ||
250 | normal[6] = modelview.m[2][0]; | ||
251 | normal[7] = modelview.m[2][1]; | ||
252 | normal[8] = modelview.m[2][2]; | ||
253 | |||
254 | glUniformMatrix4fv(m_modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]); | ||
255 | glUniformMatrix4fv(m_modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]); | ||
256 | glUniformMatrix3fv(m_normalmatrix, 1, GL_FALSE, normal); | ||
257 | |||
258 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
259 | glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); | ||
260 | glDrawArrays(GL_TRIANGLE_STRIP, 8, 4); | ||
261 | glDrawArrays(GL_TRIANGLE_STRIP, 12, 4); | ||
262 | glDrawArrays(GL_TRIANGLE_STRIP, 16, 4); | ||
263 | glDrawArrays(GL_TRIANGLE_STRIP, 20, 4); | ||
264 | } | ||
diff --git a/kmscube/cube-gles2.h b/kmscube/cube-gles2.h new file mode 100644 index 0000000..983c133 --- /dev/null +++ b/kmscube/cube-gles2.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include <GLES2/gl2.h> | ||
4 | |||
5 | class GlScene | ||
6 | { | ||
7 | public: | ||
8 | GlScene(); | ||
9 | |||
10 | GlScene(const GlScene& other) = delete; | ||
11 | GlScene& operator=(const GlScene& other) = delete; | ||
12 | |||
13 | void set_viewport(uint32_t width, uint32_t height); | ||
14 | |||
15 | void draw(uint32_t framenum); | ||
16 | |||
17 | private: | ||
18 | GLint m_modelviewmatrix, m_modelviewprojectionmatrix, m_normalmatrix; | ||
19 | |||
20 | uint32_t m_width; | ||
21 | uint32_t m_height; | ||
22 | }; | ||
diff --git a/kmscube/cube-null.cpp b/kmscube/cube-null.cpp new file mode 100644 index 0000000..f8f2091 --- /dev/null +++ b/kmscube/cube-null.cpp | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <poll.h> | ||
2 | |||
3 | #include "cube-egl.h" | ||
4 | #include "cube-gles2.h" | ||
5 | #include "cube.h" | ||
6 | |||
7 | #include <kms++util/kms++util.h> | ||
8 | |||
9 | using namespace std; | ||
10 | |||
11 | void main_null() | ||
12 | { | ||
13 | EglState egl(EGL_DEFAULT_DISPLAY); | ||
14 | EglSurface surface(egl, 0); | ||
15 | GlScene scene; | ||
16 | |||
17 | scene.set_viewport(600, 600); | ||
18 | |||
19 | int framenum = 0; | ||
20 | |||
21 | struct pollfd fds[1] = { }; | ||
22 | fds[0].fd = 0; | ||
23 | fds[0].events = POLLIN; | ||
24 | |||
25 | while (true) { | ||
26 | int r = poll(fds, ARRAY_SIZE(fds), 0); | ||
27 | ASSERT(r >= 0); | ||
28 | |||
29 | if (fds[0].revents) | ||
30 | break; | ||
31 | |||
32 | surface.make_current(); | ||
33 | scene.draw(framenum++); | ||
34 | surface.swap_buffers(); | ||
35 | } | ||
36 | } | ||
diff --git a/kmscube/cube-wl.cpp b/kmscube/cube-wl.cpp new file mode 100644 index 0000000..de75f88 --- /dev/null +++ b/kmscube/cube-wl.cpp | |||
@@ -0,0 +1,108 @@ | |||
1 | |||
2 | #include <wayland-client.h> | ||
3 | #include <wayland-egl.h> | ||
4 | |||
5 | #include <string.h> | ||
6 | #include <stdio.h> | ||
7 | |||
8 | #include "cube.h" | ||
9 | #include "cube-egl.h" | ||
10 | #include "cube-gles2.h" | ||
11 | |||
12 | static struct wl_compositor *s_compositor = NULL; | ||
13 | static struct wl_shell *s_shell = NULL; | ||
14 | static char s_running = 1; | ||
15 | |||
16 | struct window { | ||
17 | struct wl_surface *surface; | ||
18 | struct wl_shell_surface *shell_surface; | ||
19 | struct wl_egl_window *egl_window; | ||
20 | }; | ||
21 | |||
22 | // listeners | ||
23 | static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) | ||
24 | { | ||
25 | if (!strcmp(interface, "wl_compositor")) | ||
26 | s_compositor = (struct wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 0); | ||
27 | else if (!strcmp(interface, "wl_shell")) | ||
28 | s_shell = (struct wl_shell*)wl_registry_bind(registry, name, &wl_shell_interface, 0); | ||
29 | } | ||
30 | |||
31 | static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) | ||
32 | { | ||
33 | |||
34 | } | ||
35 | |||
36 | static struct wl_registry_listener registry_listener = { ®istry_add_object, ®istry_remove_object }; | ||
37 | |||
38 | static void shell_surface_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial) | ||
39 | { | ||
40 | wl_shell_surface_pong(shell_surface, serial); | ||
41 | } | ||
42 | |||
43 | static void shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) | ||
44 | { | ||
45 | struct window *window = (struct window*)data; | ||
46 | |||
47 | wl_egl_window_resize(window->egl_window, width, height, 0, 0); | ||
48 | } | ||
49 | |||
50 | static void shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface) | ||
51 | { | ||
52 | |||
53 | } | ||
54 | |||
55 | static struct wl_shell_surface_listener shell_surface_listener = { | ||
56 | &shell_surface_ping, &shell_surface_configure, &shell_surface_popup_done | ||
57 | }; | ||
58 | |||
59 | static void create_window(struct window *window, int32_t width, int32_t height) | ||
60 | { | ||
61 | window->surface = wl_compositor_create_surface(s_compositor); | ||
62 | window->shell_surface = wl_shell_get_shell_surface(s_shell, window->surface); | ||
63 | wl_shell_surface_add_listener(window->shell_surface, &shell_surface_listener, window); | ||
64 | wl_shell_surface_set_toplevel(window->shell_surface); | ||
65 | window->egl_window = wl_egl_window_create(window->surface, width, height); | ||
66 | } | ||
67 | |||
68 | static void delete_window(struct window *window) | ||
69 | { | ||
70 | wl_egl_window_destroy(window->egl_window); | ||
71 | wl_shell_surface_destroy(window->shell_surface); | ||
72 | wl_surface_destroy(window->surface); | ||
73 | } | ||
74 | |||
75 | void main_wl() | ||
76 | { | ||
77 | struct wl_display *display = wl_display_connect(NULL); | ||
78 | struct wl_registry *registry = wl_display_get_registry(display); | ||
79 | wl_registry_add_listener(registry, ®istry_listener, NULL); | ||
80 | wl_display_roundtrip(display); | ||
81 | |||
82 | uint32_t width = 600; | ||
83 | uint32_t height = 600; | ||
84 | |||
85 | struct window window; | ||
86 | create_window(&window, width, height); | ||
87 | |||
88 | { | ||
89 | EglState egl(display); | ||
90 | EglSurface surface(egl, window.egl_window); | ||
91 | GlScene scene; | ||
92 | |||
93 | scene.set_viewport(width, height); | ||
94 | |||
95 | int framenum = 0; | ||
96 | |||
97 | while (s_running) { | ||
98 | wl_display_dispatch_pending(display); | ||
99 | |||
100 | surface.make_current(); | ||
101 | scene.draw(framenum++); | ||
102 | surface.swap_buffers(); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | delete_window(&window); | ||
107 | wl_display_disconnect(display); | ||
108 | } | ||
diff --git a/kmscube/cube-x11.cpp b/kmscube/cube-x11.cpp new file mode 100644 index 0000000..47a1d2b --- /dev/null +++ b/kmscube/cube-x11.cpp | |||
@@ -0,0 +1,155 @@ | |||
1 | |||
2 | #include <kms++util/kms++util.h> | ||
3 | |||
4 | #include "cube.h" | ||
5 | #include "cube-egl.h" | ||
6 | #include "cube-gles2.h" | ||
7 | |||
8 | #include <X11/Xlib-xcb.h> | ||
9 | #include <X11/Xlibint.h> | ||
10 | |||
11 | using namespace std; | ||
12 | |||
13 | static void main_loop(Display* dpy, xcb_connection_t *c, xcb_window_t window, uint32_t width, uint32_t height) | ||
14 | { | ||
15 | EglState egl(dpy); | ||
16 | EglSurface surface(egl, (void*)(uintptr_t)window); | ||
17 | GlScene scene; | ||
18 | |||
19 | scene.set_viewport(width, height); | ||
20 | |||
21 | unsigned framenum = 0; | ||
22 | |||
23 | surface.make_current(); | ||
24 | surface.swap_buffers(); | ||
25 | |||
26 | bool need_exit = false; | ||
27 | |||
28 | xcb_generic_event_t *event; | ||
29 | while (true) { | ||
30 | |||
31 | while ((event = xcb_poll_for_event (c))) { | ||
32 | bool handled = false; | ||
33 | uint8_t response_type = event->response_type & ~0x80; | ||
34 | |||
35 | switch (response_type) { | ||
36 | case XCB_EXPOSE: { | ||
37 | handled = true; | ||
38 | break; | ||
39 | } | ||
40 | case XCB_KEY_PRESS: { | ||
41 | handled = true; | ||
42 | |||
43 | xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event; | ||
44 | if (kp->detail == 24 || kp->detail == 9) { | ||
45 | printf("Exit due to keypress\n"); | ||
46 | need_exit = true; | ||
47 | } | ||
48 | |||
49 | break; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | if (!handled) { | ||
54 | // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any. | ||
55 | // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events. | ||
56 | |||
57 | XLockDisplay(dpy); | ||
58 | Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(dpy, response_type, NULL); | ||
59 | if (proc) { | ||
60 | XESetWireToEvent(dpy, response_type, proc); | ||
61 | XEvent dummy; | ||
62 | event->sequence = LastKnownRequestProcessed(dpy); | ||
63 | proc(dpy, &dummy, (xEvent*)event); | ||
64 | } | ||
65 | XUnlockDisplay(dpy); | ||
66 | } | ||
67 | |||
68 | free(event); | ||
69 | } | ||
70 | |||
71 | if (s_num_frames && framenum >= s_num_frames) | ||
72 | need_exit = true; | ||
73 | |||
74 | if (need_exit) | ||
75 | break; | ||
76 | |||
77 | // this should be in XCB_EXPOSE, but we don't get the event after swaps... | ||
78 | scene.draw(framenum++); | ||
79 | surface.swap_buffers(); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | void main_x11() | ||
84 | { | ||
85 | Display* dpy = XOpenDisplay(NULL); | ||
86 | FAIL_IF(!dpy, "Failed to connect to the X server"); | ||
87 | |||
88 | xcb_connection_t *c = XGetXCBConnection(dpy); | ||
89 | |||
90 | /* Acquire event queue ownership */ | ||
91 | XSetEventQueueOwner(dpy, XCBOwnsEventQueue); | ||
92 | |||
93 | /* Get the first screen */ | ||
94 | const xcb_setup_t *setup = xcb_get_setup (c); | ||
95 | xcb_screen_t *screen = xcb_setup_roots_iterator (setup).data; | ||
96 | |||
97 | /* Create the window */ | ||
98 | |||
99 | uint32_t width; | ||
100 | uint32_t height; | ||
101 | |||
102 | if (s_fullscreen) { | ||
103 | width = screen->width_in_pixels; | ||
104 | height = screen->height_in_pixels; | ||
105 | } else { | ||
106 | width = 600; | ||
107 | height = 600; | ||
108 | } | ||
109 | |||
110 | const uint32_t xcb_window_attrib_mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; | ||
111 | const uint32_t xcb_window_attrib_list[] = { | ||
112 | // OVERRIDE_REDIRECT | ||
113 | 0, | ||
114 | // EVENT_MASK | ||
115 | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS, | ||
116 | }; | ||
117 | |||
118 | xcb_window_t window = xcb_generate_id (c); | ||
119 | xcb_create_window (c, /* Connection */ | ||
120 | XCB_COPY_FROM_PARENT, /* depth (same as root)*/ | ||
121 | window, /* window Id */ | ||
122 | screen->root, /* parent window */ | ||
123 | 0, 0, /* x, y */ | ||
124 | width, height, /* width, height */ | ||
125 | 0, /* border_width */ | ||
126 | XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */ | ||
127 | screen->root_visual, /* visual */ | ||
128 | xcb_window_attrib_mask, | ||
129 | xcb_window_attrib_list); | ||
130 | |||
131 | if (s_fullscreen) | ||
132 | { | ||
133 | const char *net_wm_state = "_NET_WM_STATE"; | ||
134 | const char *net_wm_state_fullscreen = "_NET_WM_STATE_FULLSCREEN"; | ||
135 | |||
136 | xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, 0, strlen(net_wm_state), net_wm_state); | ||
137 | xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(c, cookie, 0); | ||
138 | |||
139 | xcb_intern_atom_cookie_t cookie2 = xcb_intern_atom(c, 0, strlen(net_wm_state_fullscreen), net_wm_state_fullscreen); | ||
140 | xcb_intern_atom_reply_t* reply2 = xcb_intern_atom_reply(c, cookie2, 0); | ||
141 | |||
142 | xcb_change_property(c, XCB_PROP_MODE_REPLACE, window, reply->atom, XCB_ATOM_ATOM , 32, 1, (void*)&reply2->atom); | ||
143 | } | ||
144 | |||
145 | xcb_map_window (c, window); | ||
146 | xcb_flush (c); | ||
147 | |||
148 | main_loop(dpy, c, window, width, height); | ||
149 | |||
150 | xcb_flush(c); | ||
151 | xcb_unmap_window(c, window); | ||
152 | xcb_destroy_window(c, window); | ||
153 | |||
154 | XCloseDisplay(dpy); | ||
155 | } | ||
diff --git a/kmscube/cube.cpp b/kmscube/cube.cpp new file mode 100644 index 0000000..4129554 --- /dev/null +++ b/kmscube/cube.cpp | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com> | ||
3 | * Copyright (c) 2012 Rob Clark <rob@ti.com> | ||
4 | * Copyright (c) 2015 Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial portions | ||
15 | * of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
23 | * DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | /* Based on a egl cube test app originally written by Arvin Schnell */ | ||
27 | |||
28 | #include "cube.h" | ||
29 | #include <kms++util/kms++util.h> | ||
30 | |||
31 | using namespace std; | ||
32 | |||
33 | bool s_verbose; | ||
34 | bool s_fullscreen; | ||
35 | unsigned s_num_frames; | ||
36 | |||
37 | int main(int argc, char *argv[]) | ||
38 | { | ||
39 | OptionSet optionset = { | ||
40 | Option("v|verbose", | ||
41 | [&]() | ||
42 | { | ||
43 | s_verbose = true; | ||
44 | }), | ||
45 | Option("f|fullscreen", | ||
46 | [&]() | ||
47 | { | ||
48 | s_fullscreen = true; | ||
49 | }), | ||
50 | Option("n|numframes=", | ||
51 | [&](string s) | ||
52 | { | ||
53 | s_num_frames = stoi(s); | ||
54 | }), | ||
55 | }; | ||
56 | |||
57 | optionset.parse(argc, argv); | ||
58 | |||
59 | string m; | ||
60 | |||
61 | if (optionset.params().size() == 0) | ||
62 | m = "gbm"; | ||
63 | else | ||
64 | m = optionset.params().front(); | ||
65 | |||
66 | if (m == "gbm") | ||
67 | main_gbm(); | ||
68 | else if (m == "null") | ||
69 | main_null(); | ||
70 | else if (m == "x") | ||
71 | main_x11(); | ||
72 | else if (m == "wl") | ||
73 | main_wl(); | ||
74 | else { | ||
75 | printf("Unknown mode %s\n", m.c_str()); | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | return 0; | ||
80 | } | ||
diff --git a/kmscube/cube.h b/kmscube/cube.h new file mode 100644 index 0000000..6368162 --- /dev/null +++ b/kmscube/cube.h | |||
@@ -0,0 +1,11 @@ | |||
1 | #pragma once | ||
2 | |||
3 | extern bool s_verbose; | ||
4 | extern bool s_fullscreen; | ||
5 | extern unsigned s_num_frames; | ||
6 | |||
7 | void main_null(); | ||
8 | void main_gbm(); | ||
9 | void main_x11(); | ||
10 | void main_wl(); | ||
11 | |||
diff --git a/kmscube/esTransform.c b/kmscube/esTransform.c new file mode 100644 index 0000000..899c038 --- /dev/null +++ b/kmscube/esTransform.c | |||
@@ -0,0 +1,235 @@ | |||
1 | // | ||
2 | // Book: OpenGL(R) ES 2.0 Programming Guide | ||
3 | // Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner | ||
4 | // ISBN-10: 0321502795 | ||
5 | // ISBN-13: 9780321502797 | ||
6 | // Publisher: Addison-Wesley Professional | ||
7 | // URLs: http://safari.informit.com/9780321563835 | ||
8 | // http://www.opengles-book.com | ||
9 | // | ||
10 | |||
11 | /* | ||
12 | * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner | ||
13 | * | ||
14 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
15 | * copy of this software and associated documentation files (the "Software"), | ||
16 | * to deal in the Software without restriction, including without limitation | ||
17 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
18 | * and/or sell copies of the Software, and to permit persons to whom the | ||
19 | * Software is furnished to do so, subject to the following conditions: | ||
20 | * | ||
21 | * The above copyright notice and this permission notice shall be included | ||
22 | * in all copies or substantial portions of the Software. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
29 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
30 | * DEALINGS IN THE SOFTWARE. | ||
31 | */ | ||
32 | |||
33 | // ESUtil.c | ||
34 | // | ||
35 | // A utility library for OpenGL ES. This library provides a | ||
36 | // basic common framework for the example applications in the | ||
37 | // OpenGL ES 2.0 Programming Guide. | ||
38 | // | ||
39 | |||
40 | /// | ||
41 | // Includes | ||
42 | // | ||
43 | #include "esTransform.h" | ||
44 | #include <math.h> | ||
45 | #include <string.h> | ||
46 | |||
47 | #define PI 3.1415926535897932384626433832795f | ||
48 | |||
49 | void | ||
50 | esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz) | ||
51 | { | ||
52 | result->m[0][0] *= sx; | ||
53 | result->m[0][1] *= sx; | ||
54 | result->m[0][2] *= sx; | ||
55 | result->m[0][3] *= sx; | ||
56 | |||
57 | result->m[1][0] *= sy; | ||
58 | result->m[1][1] *= sy; | ||
59 | result->m[1][2] *= sy; | ||
60 | result->m[1][3] *= sy; | ||
61 | |||
62 | result->m[2][0] *= sz; | ||
63 | result->m[2][1] *= sz; | ||
64 | result->m[2][2] *= sz; | ||
65 | result->m[2][3] *= sz; | ||
66 | } | ||
67 | |||
68 | void | ||
69 | esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz) | ||
70 | { | ||
71 | result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz); | ||
72 | result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz); | ||
73 | result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz); | ||
74 | result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz); | ||
75 | } | ||
76 | |||
77 | void | ||
78 | esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) | ||
79 | { | ||
80 | GLfloat sinAngle, cosAngle; | ||
81 | GLfloat mag = sqrtf(x * x + y * y + z * z); | ||
82 | |||
83 | sinAngle = sinf ( angle * PI / 180.0f ); | ||
84 | cosAngle = cosf ( angle * PI / 180.0f ); | ||
85 | if ( mag > 0.0f ) | ||
86 | { | ||
87 | GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs; | ||
88 | GLfloat oneMinusCos; | ||
89 | ESMatrix rotMat; | ||
90 | |||
91 | x /= mag; | ||
92 | y /= mag; | ||
93 | z /= mag; | ||
94 | |||
95 | xx = x * x; | ||
96 | yy = y * y; | ||
97 | zz = z * z; | ||
98 | xy = x * y; | ||
99 | yz = y * z; | ||
100 | zx = z * x; | ||
101 | xs = x * sinAngle; | ||
102 | ys = y * sinAngle; | ||
103 | zs = z * sinAngle; | ||
104 | oneMinusCos = 1.0f - cosAngle; | ||
105 | |||
106 | rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle; | ||
107 | rotMat.m[0][1] = (oneMinusCos * xy) - zs; | ||
108 | rotMat.m[0][2] = (oneMinusCos * zx) + ys; | ||
109 | rotMat.m[0][3] = 0.0F; | ||
110 | |||
111 | rotMat.m[1][0] = (oneMinusCos * xy) + zs; | ||
112 | rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle; | ||
113 | rotMat.m[1][2] = (oneMinusCos * yz) - xs; | ||
114 | rotMat.m[1][3] = 0.0F; | ||
115 | |||
116 | rotMat.m[2][0] = (oneMinusCos * zx) - ys; | ||
117 | rotMat.m[2][1] = (oneMinusCos * yz) + xs; | ||
118 | rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle; | ||
119 | rotMat.m[2][3] = 0.0F; | ||
120 | |||
121 | rotMat.m[3][0] = 0.0F; | ||
122 | rotMat.m[3][1] = 0.0F; | ||
123 | rotMat.m[3][2] = 0.0F; | ||
124 | rotMat.m[3][3] = 1.0F; | ||
125 | |||
126 | esMatrixMultiply( result, &rotMat, result ); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | void | ||
131 | esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ) | ||
132 | { | ||
133 | float deltaX = right - left; | ||
134 | float deltaY = top - bottom; | ||
135 | float deltaZ = farZ - nearZ; | ||
136 | ESMatrix frust; | ||
137 | |||
138 | if ( (nearZ <= 0.0f) || (farZ <= 0.0f) || | ||
139 | (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) ) | ||
140 | return; | ||
141 | |||
142 | frust.m[0][0] = 2.0f * nearZ / deltaX; | ||
143 | frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f; | ||
144 | |||
145 | frust.m[1][1] = 2.0f * nearZ / deltaY; | ||
146 | frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f; | ||
147 | |||
148 | frust.m[2][0] = (right + left) / deltaX; | ||
149 | frust.m[2][1] = (top + bottom) / deltaY; | ||
150 | frust.m[2][2] = -(nearZ + farZ) / deltaZ; | ||
151 | frust.m[2][3] = -1.0f; | ||
152 | |||
153 | frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ; | ||
154 | frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f; | ||
155 | |||
156 | esMatrixMultiply(result, &frust, result); | ||
157 | } | ||
158 | |||
159 | |||
160 | void | ||
161 | esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ) | ||
162 | { | ||
163 | GLfloat frustumW, frustumH; | ||
164 | |||
165 | frustumH = tanf( fovy / 360.0f * PI ) * nearZ; | ||
166 | frustumW = frustumH * aspect; | ||
167 | |||
168 | esFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ ); | ||
169 | } | ||
170 | |||
171 | void | ||
172 | esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ) | ||
173 | { | ||
174 | float deltaX = right - left; | ||
175 | float deltaY = top - bottom; | ||
176 | float deltaZ = farZ - nearZ; | ||
177 | ESMatrix ortho; | ||
178 | |||
179 | if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) ) | ||
180 | return; | ||
181 | |||
182 | esMatrixLoadIdentity(&ortho); | ||
183 | ortho.m[0][0] = 2.0f / deltaX; | ||
184 | ortho.m[3][0] = -(right + left) / deltaX; | ||
185 | ortho.m[1][1] = 2.0f / deltaY; | ||
186 | ortho.m[3][1] = -(top + bottom) / deltaY; | ||
187 | ortho.m[2][2] = -2.0f / deltaZ; | ||
188 | ortho.m[3][2] = -(nearZ + farZ) / deltaZ; | ||
189 | |||
190 | esMatrixMultiply(result, &ortho, result); | ||
191 | } | ||
192 | |||
193 | |||
194 | void | ||
195 | esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB) | ||
196 | { | ||
197 | ESMatrix tmp; | ||
198 | int i; | ||
199 | |||
200 | for (i=0; i<4; i++) | ||
201 | { | ||
202 | tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) + | ||
203 | (srcA->m[i][1] * srcB->m[1][0]) + | ||
204 | (srcA->m[i][2] * srcB->m[2][0]) + | ||
205 | (srcA->m[i][3] * srcB->m[3][0]) ; | ||
206 | |||
207 | tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) + | ||
208 | (srcA->m[i][1] * srcB->m[1][1]) + | ||
209 | (srcA->m[i][2] * srcB->m[2][1]) + | ||
210 | (srcA->m[i][3] * srcB->m[3][1]) ; | ||
211 | |||
212 | tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) + | ||
213 | (srcA->m[i][1] * srcB->m[1][2]) + | ||
214 | (srcA->m[i][2] * srcB->m[2][2]) + | ||
215 | (srcA->m[i][3] * srcB->m[3][2]) ; | ||
216 | |||
217 | tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) + | ||
218 | (srcA->m[i][1] * srcB->m[1][3]) + | ||
219 | (srcA->m[i][2] * srcB->m[2][3]) + | ||
220 | (srcA->m[i][3] * srcB->m[3][3]) ; | ||
221 | } | ||
222 | memcpy(result, &tmp, sizeof(ESMatrix)); | ||
223 | } | ||
224 | |||
225 | |||
226 | void | ||
227 | esMatrixLoadIdentity(ESMatrix *result) | ||
228 | { | ||
229 | memset(result, 0x0, sizeof(ESMatrix)); | ||
230 | result->m[0][0] = 1.0f; | ||
231 | result->m[1][1] = 1.0f; | ||
232 | result->m[2][2] = 1.0f; | ||
233 | result->m[3][3] = 1.0f; | ||
234 | } | ||
235 | |||
diff --git a/kmscube/esTransform.h b/kmscube/esTransform.h new file mode 100644 index 0000000..c0383ab --- /dev/null +++ b/kmscube/esTransform.h | |||
@@ -0,0 +1,125 @@ | |||
1 | // | ||
2 | // Book: OpenGL(R) ES 2.0 Programming Guide | ||
3 | // Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner | ||
4 | // ISBN-10: 0321502795 | ||
5 | // ISBN-13: 9780321502797 | ||
6 | // Publisher: Addison-Wesley Professional | ||
7 | // URLs: http://safari.informit.com/9780321563835 | ||
8 | // http://www.opengles-book.com | ||
9 | // | ||
10 | |||
11 | /* | ||
12 | * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner | ||
13 | * | ||
14 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
15 | * copy of this software and associated documentation files (the "Software"), | ||
16 | * to deal in the Software without restriction, including without limitation | ||
17 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
18 | * and/or sell copies of the Software, and to permit persons to whom the | ||
19 | * Software is furnished to do so, subject to the following conditions: | ||
20 | * | ||
21 | * The above copyright notice and this permission notice shall be included | ||
22 | * in all copies or substantial portions of the Software. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
29 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
30 | * DEALINGS IN THE SOFTWARE. | ||
31 | */ | ||
32 | |||
33 | // | ||
34 | /// \file ESUtil.h | ||
35 | /// \brief A utility library for OpenGL ES. This library provides a | ||
36 | /// basic common framework for the example applications in the | ||
37 | /// OpenGL ES 2.0 Programming Guide. | ||
38 | // | ||
39 | #ifndef ESUTIL_H | ||
40 | #define ESUTIL_H | ||
41 | |||
42 | /// | ||
43 | // Includes | ||
44 | // | ||
45 | #include <GLES2/gl2.h> | ||
46 | #include <EGL/egl.h> | ||
47 | |||
48 | #ifdef __cplusplus | ||
49 | |||
50 | extern "C" { | ||
51 | #endif | ||
52 | |||
53 | typedef struct | ||
54 | { | ||
55 | GLfloat m[4][4]; | ||
56 | } ESMatrix; | ||
57 | |||
58 | // | ||
59 | /// \brief multiply matrix specified by result with a scaling matrix and return new matrix in result | ||
60 | /// \param result Specifies the input matrix. Scaled matrix is returned in result. | ||
61 | /// \param sx, sy, sz Scale factors along the x, y and z axes respectively | ||
62 | // | ||
63 | void esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz); | ||
64 | |||
65 | // | ||
66 | /// \brief multiply matrix specified by result with a translation matrix and return new matrix in result | ||
67 | /// \param result Specifies the input matrix. Translated matrix is returned in result. | ||
68 | /// \param tx, ty, tz Scale factors along the x, y and z axes respectively | ||
69 | // | ||
70 | void esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz); | ||
71 | |||
72 | // | ||
73 | /// \brief multiply matrix specified by result with a rotation matrix and return new matrix in result | ||
74 | /// \param result Specifies the input matrix. Rotated matrix is returned in result. | ||
75 | /// \param angle Specifies the angle of rotation, in degrees. | ||
76 | /// \param x, y, z Specify the x, y and z coordinates of a vector, respectively | ||
77 | // | ||
78 | void esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); | ||
79 | |||
80 | // | ||
81 | // \brief multiply matrix specified by result with a perspective matrix and return new matrix in result | ||
82 | /// \param result Specifies the input matrix. new matrix is returned in result. | ||
83 | /// \param left, right Coordinates for the left and right vertical clipping planes | ||
84 | /// \param bottom, top Coordinates for the bottom and top horizontal clipping planes | ||
85 | /// \param nearZ, farZ Distances to the near and far depth clipping planes. Both distances must be positive. | ||
86 | // | ||
87 | void esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ); | ||
88 | |||
89 | // | ||
90 | /// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result | ||
91 | /// \param result Specifies the input matrix. new matrix is returned in result. | ||
92 | /// \param fovy Field of view y angle in degrees | ||
93 | /// \param aspect Aspect ratio of screen | ||
94 | /// \param nearZ Near plane distance | ||
95 | /// \param farZ Far plane distance | ||
96 | // | ||
97 | void esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ); | ||
98 | |||
99 | // | ||
100 | /// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result | ||
101 | /// \param result Specifies the input matrix. new matrix is returned in result. | ||
102 | /// \param left, right Coordinates for the left and right vertical clipping planes | ||
103 | /// \param bottom, top Coordinates for the bottom and top horizontal clipping planes | ||
104 | /// \param nearZ, farZ Distances to the near and far depth clipping planes. These values are negative if plane is behind the viewer | ||
105 | // | ||
106 | void esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ); | ||
107 | |||
108 | // | ||
109 | /// \brief perform the following operation - result matrix = srcA matrix * srcB matrix | ||
110 | /// \param result Returns multiplied matrix | ||
111 | /// \param srcA, srcB Input matrices to be multiplied | ||
112 | // | ||
113 | void esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB); | ||
114 | |||
115 | // | ||
116 | //// \brief return an indentity matrix | ||
117 | //// \param result returns identity matrix | ||
118 | // | ||
119 | void esMatrixLoadIdentity(ESMatrix *result); | ||
120 | |||
121 | #ifdef __cplusplus | ||
122 | } | ||
123 | #endif | ||
124 | |||
125 | #endif // ESUTIL_H | ||
diff --git a/py/CMakeLists.txt b/py/CMakeLists.txt new file mode 100644 index 0000000..77f19b4 --- /dev/null +++ b/py/CMakeLists.txt | |||
@@ -0,0 +1,2 @@ | |||
1 | add_subdirectory(pykms) | ||
2 | add_subdirectory(tests) | ||
diff --git a/py/pykms/CMakeLists.txt b/py/pykms/CMakeLists.txt new file mode 100644 index 0000000..505c0c3 --- /dev/null +++ b/py/pykms/CMakeLists.txt | |||
@@ -0,0 +1,30 @@ | |||
1 | include_directories(${LIBDRM_INCLUDE_DIRS}) | ||
2 | link_directories(${LIBDRM_LIBRARY_DIRS}) | ||
3 | |||
4 | pkg_search_module(PYTHON REQUIRED ${KMSXX_PYTHON_VERSION}) | ||
5 | include_directories(${PYTHON_INCLUDE_DIRS}) | ||
6 | |||
7 | if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) | ||
8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") | ||
9 | endif() | ||
10 | |||
11 | include_directories(${PROJECT_SOURCE_DIR}/ext/pybind11/include) | ||
12 | |||
13 | set(SRCS pykms.cpp pykmsbase.cpp pykmsutil.cpp pyvid.cpp) | ||
14 | |||
15 | if(LIBDRM_OMAP_FOUND) | ||
16 | set(SRCS ${SRCS} pykmsomap.cpp) | ||
17 | endif() | ||
18 | |||
19 | add_library(pykms SHARED ${SRCS}) | ||
20 | target_link_libraries(pykms kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
21 | |||
22 | # Don't add a 'lib' prefix to the shared library | ||
23 | set_target_properties(pykms PROPERTIES PREFIX "") | ||
24 | set_target_properties(pykms PROPERTIES LIBRARY_OUTPUT_DIRECTORY "") | ||
25 | |||
26 | configure_file(__init__.py __init__.py COPYONLY) | ||
27 | |||
28 | set(PY_DESTDIR lib/python${PYTHON_VERSION}/site-packages/pykms) | ||
29 | install(FILES __init__.py DESTINATION ${PY_DESTDIR}) | ||
30 | install(TARGETS pykms DESTINATION ${PY_DESTDIR}) | ||
diff --git a/py/pykms/__init__.py b/py/pykms/__init__.py new file mode 100644 index 0000000..746c917 --- /dev/null +++ b/py/pykms/__init__.py | |||
@@ -0,0 +1,139 @@ | |||
1 | from .pykms import * | ||
2 | from enum import Enum | ||
3 | import os | ||
4 | import struct | ||
5 | |||
6 | # | ||
7 | # Common RGB colours | ||
8 | # | ||
9 | |||
10 | red = RGB(255, 0, 0) | ||
11 | green = RGB(0, 255, 0) | ||
12 | blue = RGB(0, 0, 255) | ||
13 | yellow = RGB(255, 255, 0) | ||
14 | purple = RGB(255, 0, 255) | ||
15 | white = RGB(255, 255, 255) | ||
16 | cyan = RGB(0, 255, 255) | ||
17 | |||
18 | # | ||
19 | # Rotation enum | ||
20 | # | ||
21 | |||
22 | class Rotation(int, Enum): | ||
23 | ROTATE_0 = 1 << 0 | ||
24 | ROTATE_90 = 1 << 1 | ||
25 | ROTATE_180 = 1 << 2 | ||
26 | ROTATE_270 = 1 << 3 | ||
27 | ROTATE_MASK = ROTATE_0 | ROTATE_90 | ROTATE_180 | ROTATE_270 | ||
28 | REFLECT_X = 1 << 4 | ||
29 | REFLECT_Y = 1 << 5 | ||
30 | REFLECT_MASK = REFLECT_X | REFLECT_Y | ||
31 | |||
32 | # | ||
33 | # DrmObject API extensions | ||
34 | # | ||
35 | |||
36 | def __obj_set_prop(self, prop, value): | ||
37 | if self.card.has_atomic: | ||
38 | areq = AtomicReq(self.card) | ||
39 | areq.add(self, prop, value) | ||
40 | if areq.commit_sync() != 0: | ||
41 | print("commit failed") | ||
42 | else: | ||
43 | if self.set_prop_value(prop, value) != 0: | ||
44 | print("setting property failed") | ||
45 | |||
46 | def __obj_set_props(self, map): | ||
47 | if self.card.has_atomic: | ||
48 | areq = AtomicReq(self.card) | ||
49 | |||
50 | for key, value in map.items(): | ||
51 | areq.add(self, key, value) | ||
52 | |||
53 | if areq.commit_sync() != 0: | ||
54 | print("commit failed") | ||
55 | else: | ||
56 | for propid,propval in map.items(): | ||
57 | if self.set_prop_value(propid, propval) != 0: | ||
58 | print("setting property failed") | ||
59 | |||
60 | DrmObject.set_prop = __obj_set_prop | ||
61 | DrmObject.set_props = __obj_set_props | ||
62 | |||
63 | # | ||
64 | # Card API extensions | ||
65 | # | ||
66 | |||
67 | def __card_disable_planes(self): | ||
68 | areq = AtomicReq(self) | ||
69 | |||
70 | for p in self.planes: | ||
71 | areq.add(p, "FB_ID", 0) | ||
72 | areq.add(p, "CRTC_ID", 0) | ||
73 | |||
74 | if areq.commit_sync() != 0: | ||
75 | print("disabling planes failed") | ||
76 | |||
77 | Card.disable_planes = __card_disable_planes | ||
78 | |||
79 | class DrmEventType(Enum): | ||
80 | VBLANK = 0x01 | ||
81 | FLIP_COMPLETE = 0x02 | ||
82 | |||
83 | # struct drm_event { | ||
84 | # __u32 type; | ||
85 | # __u32 length; | ||
86 | #}; | ||
87 | # | ||
88 | |||
89 | _drm_ev = struct.Struct("II") | ||
90 | |||
91 | #struct drm_event_vblank { | ||
92 | # struct drm_event base; | ||
93 | # __u64 user_data; | ||
94 | # __u32 tv_sec; | ||
95 | # __u32 tv_usec; | ||
96 | # __u32 sequence; | ||
97 | # __u32 reserved; | ||
98 | #}; | ||
99 | |||
100 | _drm_ev_vbl = struct.Struct("QIIII") # Note: doesn't contain drm_event | ||
101 | |||
102 | class DrmEvent: | ||
103 | def __init__(self, type, seq, time, data): | ||
104 | self.type = type | ||
105 | self.seq = seq | ||
106 | self.time = time | ||
107 | self.data = data | ||
108 | |||
109 | # Return DrmEvents. Note: blocks if there's nothing to read | ||
110 | def __card_read_events(self): | ||
111 | buf = os.read(self.fd, _drm_ev_vbl.size * 20) | ||
112 | |||
113 | if len(buf) == 0: | ||
114 | return | ||
115 | |||
116 | if len(buf) < _drm_ev.size: | ||
117 | raise RuntimeError("Partial DRM event") | ||
118 | |||
119 | idx = 0 | ||
120 | |||
121 | while idx < len(buf): | ||
122 | ev_tuple = _drm_ev.unpack_from(buf, idx) | ||
123 | |||
124 | type = DrmEventType(ev_tuple[0]) | ||
125 | |||
126 | if type != DrmEventType.VBLANK and type != DrmEventType.FLIP_COMPLETE: | ||
127 | raise RuntimeError("Illegal DRM event type") | ||
128 | |||
129 | vbl_tuple = _drm_ev_vbl.unpack_from(buf, idx + _drm_ev.size) | ||
130 | |||
131 | seq = vbl_tuple[3] | ||
132 | time = vbl_tuple[1] + vbl_tuple[2] / 1000000.0; | ||
133 | udata = vbl_tuple[0] | ||
134 | |||
135 | yield DrmEvent(type, seq, time, udata) | ||
136 | |||
137 | idx += ev_tuple[1] | ||
138 | |||
139 | Card.read_events = __card_read_events | ||
diff --git a/py/pykms/pykms.cpp b/py/pykms/pykms.cpp new file mode 100644 index 0000000..fec8417 --- /dev/null +++ b/py/pykms/pykms.cpp | |||
@@ -0,0 +1,28 @@ | |||
1 | #include <pybind11/pybind11.h> | ||
2 | #include <pybind11/stl.h> | ||
3 | #include <kms++/kms++.h> | ||
4 | |||
5 | namespace py = pybind11; | ||
6 | |||
7 | using namespace kms; | ||
8 | using namespace std; | ||
9 | |||
10 | void init_pykmstest(py::module &m); | ||
11 | void init_pykmsbase(py::module &m); | ||
12 | void init_pyvid(py::module &m); | ||
13 | |||
14 | #if HAS_LIBDRM_OMAP | ||
15 | void init_pykmsomap(py::module &m); | ||
16 | #endif | ||
17 | |||
18 | PYBIND11_MODULE(pykms, m) { | ||
19 | init_pykmsbase(m); | ||
20 | |||
21 | init_pykmstest(m); | ||
22 | |||
23 | init_pyvid(m); | ||
24 | |||
25 | #if HAS_LIBDRM_OMAP | ||
26 | init_pykmsomap(m); | ||
27 | #endif | ||
28 | } | ||
diff --git a/py/pykms/pykmsbase.cpp b/py/pykms/pykmsbase.cpp new file mode 100644 index 0000000..2c97bd7 --- /dev/null +++ b/py/pykms/pykmsbase.cpp | |||
@@ -0,0 +1,246 @@ | |||
1 | #include <pybind11/pybind11.h> | ||
2 | #include <pybind11/stl.h> | ||
3 | #include <kms++/kms++.h> | ||
4 | |||
5 | namespace py = pybind11; | ||
6 | |||
7 | using namespace kms; | ||
8 | using namespace std; | ||
9 | |||
10 | // Helper to convert vector<T*> to vector<unique_ptr<T, py::nodelete>> | ||
11 | template<typename T> | ||
12 | static vector<unique_ptr<T, py::nodelete>> convert_vector(const vector<T*>& source) | ||
13 | { | ||
14 | vector<unique_ptr<T, py::nodelete>> v; | ||
15 | for (T* p : source) | ||
16 | v.push_back(unique_ptr<T, py::nodelete>(p)); | ||
17 | return v; | ||
18 | } | ||
19 | |||
20 | void init_pykmsbase(py::module &m) | ||
21 | { | ||
22 | py::class_<Card>(m, "Card") | ||
23 | .def(py::init<>()) | ||
24 | .def_property_readonly("fd", &Card::fd) | ||
25 | .def_property_readonly("get_first_connected_connector", &Card::get_first_connected_connector) | ||
26 | |||
27 | // XXX pybind11 can't handle vector<T*> where T is non-copyable, and complains: | ||
28 | // RuntimeError: return_value_policy = move, but the object is neither movable nor copyable! | ||
29 | // So we do this manually. | ||
30 | .def_property_readonly("connectors", [](Card* self) { | ||
31 | return convert_vector(self->get_connectors()); | ||
32 | }) | ||
33 | |||
34 | .def_property_readonly("crtcs", [](Card* self) { | ||
35 | return convert_vector(self->get_crtcs()); | ||
36 | }) | ||
37 | |||
38 | .def_property_readonly("encoders", [](Card* self) { | ||
39 | return convert_vector(self->get_encoders()); | ||
40 | }) | ||
41 | |||
42 | .def_property_readonly("planes", [](Card* self) { | ||
43 | return convert_vector(self->get_planes()); | ||
44 | }) | ||
45 | |||
46 | .def_property_readonly("has_atomic", &Card::has_atomic) | ||
47 | .def("get_prop", (Property* (Card::*)(uint32_t) const)&Card::get_prop) | ||
48 | ; | ||
49 | |||
50 | py::class_<DrmObject, unique_ptr<DrmObject, py::nodelete>>(m, "DrmObject") | ||
51 | .def_property_readonly("id", &DrmObject::id) | ||
52 | .def_property_readonly("idx", &DrmObject::idx) | ||
53 | .def_property_readonly("card", &DrmObject::card) | ||
54 | ; | ||
55 | |||
56 | py::class_<DrmPropObject, DrmObject, unique_ptr<DrmPropObject, py::nodelete>>(m, "DrmPropObject") | ||
57 | .def("refresh_props", &DrmPropObject::refresh_props) | ||
58 | .def_property_readonly("prop_map", &DrmPropObject::get_prop_map) | ||
59 | .def("get_prop_value", (uint64_t (DrmPropObject::*)(const string&) const)&DrmPropObject::get_prop_value) | ||
60 | .def("set_prop_value",(int (DrmPropObject::*)(const string&, uint64_t)) &DrmPropObject::set_prop_value) | ||
61 | .def("get_prop_value_as_blob", &DrmPropObject::get_prop_value_as_blob) | ||
62 | .def("get_prop", &DrmPropObject::get_prop) | ||
63 | ; | ||
64 | |||
65 | py::class_<Connector, DrmPropObject, unique_ptr<Connector, py::nodelete>>(m, "Connector") | ||
66 | .def_property_readonly("fullname", &Connector::fullname) | ||
67 | .def("get_default_mode", &Connector::get_default_mode) | ||
68 | .def("get_current_crtc", &Connector::get_current_crtc) | ||
69 | .def("get_possible_crtcs", [](Connector* self) { | ||
70 | return convert_vector(self->get_possible_crtcs()); | ||
71 | }) | ||
72 | .def("get_modes", &Connector::get_modes) | ||
73 | .def("get_mode", (Videomode (Connector::*)(const string& mode) const)&Connector::get_mode) | ||
74 | .def("get_mode", (Videomode (Connector::*)(unsigned xres, unsigned yres, float refresh, bool ilace) const)&Connector::get_mode) | ||
75 | .def("connected", &Connector::connected) | ||
76 | .def("__repr__", [](const Connector& o) { return "<pykms.Connector " + to_string(o.id()) + ">"; }) | ||
77 | .def("refresh", &Connector::refresh) | ||
78 | ; | ||
79 | |||
80 | py::class_<Crtc, DrmPropObject, unique_ptr<Crtc, py::nodelete>>(m, "Crtc") | ||
81 | .def("set_mode", (int (Crtc::*)(Connector*, const Videomode&))&Crtc::set_mode) | ||
82 | .def("set_mode", (int (Crtc::*)(Connector*, Framebuffer&, const Videomode&))&Crtc::set_mode) | ||
83 | .def("disable_mode", &Crtc::disable_mode) | ||
84 | .def("page_flip", | ||
85 | [](Crtc* self, Framebuffer& fb, uint32_t data) | ||
86 | { | ||
87 | self->page_flip(fb, (void*)(intptr_t)data); | ||
88 | }, py::arg("fb"), py::arg("data") = 0) | ||
89 | .def("set_plane", &Crtc::set_plane) | ||
90 | .def_property_readonly("possible_planes", &Crtc::get_possible_planes) | ||
91 | .def_property_readonly("primary_plane", &Crtc::get_primary_plane) | ||
92 | .def_property_readonly("mode", &Crtc::mode) | ||
93 | .def_property_readonly("mode_valid", &Crtc::mode_valid) | ||
94 | .def("__repr__", [](const Crtc& o) { return "<pykms.Crtc " + to_string(o.id()) + ">"; }) | ||
95 | .def("refresh", &Crtc::refresh) | ||
96 | ; | ||
97 | |||
98 | py::class_<Encoder, DrmPropObject, unique_ptr<Encoder, py::nodelete>>(m, "Encoder") | ||
99 | .def("refresh", &Encoder::refresh) | ||
100 | ; | ||
101 | |||
102 | py::class_<Plane, DrmPropObject, unique_ptr<Plane, py::nodelete>>(m, "Plane") | ||
103 | .def("supports_crtc", &Plane::supports_crtc) | ||
104 | .def_property_readonly("formats", &Plane::get_formats) | ||
105 | .def_property_readonly("plane_type", &Plane::plane_type) | ||
106 | .def("__repr__", [](const Plane& o) { return "<pykms.Plane " + to_string(o.id()) + ">"; }) | ||
107 | ; | ||
108 | |||
109 | py::enum_<PlaneType>(m, "PlaneType") | ||
110 | .value("Overlay", PlaneType::Overlay) | ||
111 | .value("Primary", PlaneType::Primary) | ||
112 | .value("Cursor", PlaneType::Cursor) | ||
113 | ; | ||
114 | |||
115 | py::class_<Property, DrmObject, unique_ptr<Property, py::nodelete>>(m, "Property") | ||
116 | .def_property_readonly("name", &Property::name) | ||
117 | .def_property_readonly("enums", &Property::get_enums) | ||
118 | ; | ||
119 | |||
120 | py::class_<Blob>(m, "Blob") | ||
121 | .def("__init__", [](Blob& instance, Card& card, py::buffer buf) { | ||
122 | py::buffer_info info = buf.request(); | ||
123 | if (info.ndim != 1) | ||
124 | throw std::runtime_error("Incompatible buffer dimension!"); | ||
125 | |||
126 | new (&instance) Blob(card, info.ptr, info.size * info.itemsize); | ||
127 | }, | ||
128 | py::keep_alive<1, 3>()) // Keep Card alive until this is destructed | ||
129 | |||
130 | .def_property_readonly("data", &Blob::data) | ||
131 | |||
132 | // XXX pybind11 doesn't support a base object (DrmObject) with custom holder-type, | ||
133 | // and a subclass with standard holder-type. | ||
134 | // So we just copy the DrmObject members here. | ||
135 | // Note that this means that python thinks we don't derive from DrmObject | ||
136 | .def_property_readonly("id", &DrmObject::id) | ||
137 | .def_property_readonly("idx", &DrmObject::idx) | ||
138 | .def_property_readonly("card", &DrmObject::card) | ||
139 | ; | ||
140 | |||
141 | py::class_<Framebuffer>(m, "Framebuffer") | ||
142 | .def_property_readonly("width", &Framebuffer::width) | ||
143 | .def_property_readonly("height", &Framebuffer::height) | ||
144 | |||
145 | // XXX pybind11 doesn't support a base object (DrmObject) with custom holder-type, | ||
146 | // and a subclass with standard holder-type. | ||
147 | // So we just copy the DrmObject members here. | ||
148 | // Note that this means that python thinks we don't derive from DrmObject | ||
149 | .def_property_readonly("id", &DrmObject::id) | ||
150 | .def_property_readonly("idx", &DrmObject::idx) | ||
151 | .def_property_readonly("card", &DrmObject::card) | ||
152 | ; | ||
153 | |||
154 | py::class_<DumbFramebuffer, Framebuffer>(m, "DumbFramebuffer") | ||
155 | .def(py::init<Card&, uint32_t, uint32_t, const string&>(), | ||
156 | py::keep_alive<1, 2>()) // Keep Card alive until this is destructed | ||
157 | .def(py::init<Card&, uint32_t, uint32_t, PixelFormat>(), | ||
158 | py::keep_alive<1, 2>()) // Keep Card alive until this is destructed | ||
159 | .def_property_readonly("format", &DumbFramebuffer::format) | ||
160 | .def_property_readonly("num_planes", &DumbFramebuffer::num_planes) | ||
161 | .def("fd", &DumbFramebuffer::prime_fd) | ||
162 | .def("stride", &DumbFramebuffer::stride) | ||
163 | .def("offset", &DumbFramebuffer::offset) | ||
164 | ; | ||
165 | |||
166 | py::class_<ExtFramebuffer, Framebuffer>(m, "ExtFramebuffer") | ||
167 | .def(py::init<Card&, uint32_t, uint32_t, PixelFormat, vector<int>, vector<uint32_t>, vector<uint32_t>>(), | ||
168 | py::keep_alive<1, 2>()) // Keep Card alive until this is destructed | ||
169 | ; | ||
170 | |||
171 | py::enum_<PixelFormat>(m, "PixelFormat") | ||
172 | .value("Undefined", PixelFormat::Undefined) | ||
173 | |||
174 | .value("NV12", PixelFormat::NV12) | ||
175 | .value("NV21", PixelFormat::NV21) | ||
176 | |||
177 | .value("UYVY", PixelFormat::UYVY) | ||
178 | .value("YUYV", PixelFormat::YUYV) | ||
179 | .value("YVYU", PixelFormat::YVYU) | ||
180 | .value("VYUY", PixelFormat::VYUY) | ||
181 | |||
182 | .value("XRGB8888", PixelFormat::XRGB8888) | ||
183 | .value("XBGR8888", PixelFormat::XBGR8888) | ||
184 | .value("ARGB8888", PixelFormat::ARGB8888) | ||
185 | .value("ABGR8888", PixelFormat::ABGR8888) | ||
186 | |||
187 | .value("RGB888", PixelFormat::RGB888) | ||
188 | .value("BGR888", PixelFormat::BGR888) | ||
189 | |||
190 | .value("RGB565", PixelFormat::RGB565) | ||
191 | .value("BGR565", PixelFormat::BGR565) | ||
192 | ; | ||
193 | |||
194 | py::enum_<SyncPolarity>(m, "SyncPolarity") | ||
195 | .value("Undefined", SyncPolarity::Undefined) | ||
196 | .value("Positive", SyncPolarity::Positive) | ||
197 | .value("Negative", SyncPolarity::Negative) | ||
198 | ; | ||
199 | |||
200 | py::class_<Videomode>(m, "Videomode") | ||
201 | .def(py::init<>()) | ||
202 | |||
203 | .def_readwrite("name", &Videomode::name) | ||
204 | |||
205 | .def_readwrite("clock", &Videomode::clock) | ||
206 | |||
207 | .def_readwrite("hdisplay", &Videomode::hdisplay) | ||
208 | .def_readwrite("hsync_start", &Videomode::hsync_start) | ||
209 | .def_readwrite("hsync_end", &Videomode::hsync_end) | ||
210 | .def_readwrite("htotal", &Videomode::htotal) | ||
211 | |||
212 | .def_readwrite("vdisplay", &Videomode::vdisplay) | ||
213 | .def_readwrite("vsync_start", &Videomode::vsync_start) | ||
214 | .def_readwrite("vsync_end", &Videomode::vsync_end) | ||
215 | .def_readwrite("vtotal", &Videomode::vtotal) | ||
216 | |||
217 | .def_readwrite("vrefresh", &Videomode::vrefresh) | ||
218 | |||
219 | .def_readwrite("flags", &Videomode::flags) | ||
220 | .def_readwrite("type", &Videomode::type) | ||
221 | |||
222 | .def("__repr__", [](const Videomode& vm) { return "<pykms.Videomode " + to_string(vm.hdisplay) + "x" + to_string(vm.vdisplay) + ">"; }) | ||
223 | |||
224 | .def("to_blob", &Videomode::to_blob) | ||
225 | |||
226 | .def_property("hsync", &Videomode::hsync, &Videomode::set_hsync) | ||
227 | .def_property("vsync", &Videomode::vsync, &Videomode::set_vsync) | ||
228 | ; | ||
229 | |||
230 | |||
231 | m.def("videomode_from_timings", &videomode_from_timings); | ||
232 | |||
233 | py::class_<AtomicReq>(m, "AtomicReq") | ||
234 | .def(py::init<Card&>(), | ||
235 | py::keep_alive<1, 2>()) // Keep Card alive until this is destructed | ||
236 | .def("add", (void (AtomicReq::*)(DrmPropObject*, const string&, uint64_t)) &AtomicReq::add) | ||
237 | .def("add", (void (AtomicReq::*)(DrmPropObject*, const map<string, uint64_t>&)) &AtomicReq::add) | ||
238 | .def("test", &AtomicReq::test, py::arg("allow_modeset") = false) | ||
239 | .def("commit", | ||
240 | [](AtomicReq* self, uint32_t data, bool allow) | ||
241 | { | ||
242 | return self->commit((void*)(intptr_t)data, allow); | ||
243 | }, py::arg("data") = 0, py::arg("allow_modeset") = false) | ||
244 | .def("commit_sync", &AtomicReq::commit_sync, py::arg("allow_modeset") = false) | ||
245 | ; | ||
246 | } | ||
diff --git a/py/pykms/pykmsomap.cpp b/py/pykms/pykmsomap.cpp new file mode 100644 index 0000000..4fc7084 --- /dev/null +++ b/py/pykms/pykmsomap.cpp | |||
@@ -0,0 +1,42 @@ | |||
1 | #include <pybind11/pybind11.h> | ||
2 | #include <pybind11/stl.h> | ||
3 | #include <kms++/kms++.h> | ||
4 | #include <kms++/omap/omapkms++.h> | ||
5 | |||
6 | namespace py = pybind11; | ||
7 | |||
8 | using namespace kms; | ||
9 | using namespace std; | ||
10 | |||
11 | void init_pykmsomap(py::module &m) | ||
12 | { | ||
13 | py::class_<OmapCard, Card>(m, "OmapCard") | ||
14 | .def(py::init<>()) | ||
15 | ; | ||
16 | |||
17 | py::class_<OmapFramebuffer, Framebuffer> omapfb(m, "OmapFramebuffer"); | ||
18 | |||
19 | // XXX we should use py::arithmetic() here to support or and and operators, but it's not supported in the pybind11 we use | ||
20 | py::enum_<OmapFramebuffer::Flags>(omapfb, "Flags") | ||
21 | .value("None", OmapFramebuffer::Flags::None) | ||
22 | .value("Tiled", OmapFramebuffer::Flags::Tiled) | ||
23 | .value("MemContig", OmapFramebuffer::Flags::MemContig) | ||
24 | .value("MemTiler", OmapFramebuffer::Flags::MemTiler) | ||
25 | .value("MemPin", OmapFramebuffer::Flags::MemPin) | ||
26 | .export_values() | ||
27 | ; | ||
28 | |||
29 | omapfb | ||
30 | .def(py::init<OmapCard&, uint32_t, uint32_t, const string&, OmapFramebuffer::Flags>(), | ||
31 | py::keep_alive<1, 2>(), // Keep Card alive until this is destructed | ||
32 | py::arg("card"), py::arg("width"), py::arg("height"), py::arg("fourcc"), py::arg("flags") = OmapFramebuffer::None) | ||
33 | .def(py::init<OmapCard&, uint32_t, uint32_t, PixelFormat, OmapFramebuffer::Flags>(), | ||
34 | py::keep_alive<1, 2>(), // Keep OmapCard alive until this is destructed | ||
35 | py::arg("card"), py::arg("width"), py::arg("height"), py::arg("pixfmt"), py::arg("flags") = OmapFramebuffer::None) | ||
36 | .def_property_readonly("format", &OmapFramebuffer::format) | ||
37 | .def_property_readonly("num_planes", &OmapFramebuffer::num_planes) | ||
38 | .def("fd", &OmapFramebuffer::prime_fd) | ||
39 | .def("stride", &OmapFramebuffer::stride) | ||
40 | .def("offset", &OmapFramebuffer::offset) | ||
41 | ; | ||
42 | } | ||
diff --git a/py/pykms/pykmsutil.cpp b/py/pykms/pykmsutil.cpp new file mode 100644 index 0000000..518d5ea --- /dev/null +++ b/py/pykms/pykmsutil.cpp | |||
@@ -0,0 +1,62 @@ | |||
1 | #include <pybind11/pybind11.h> | ||
2 | #include <pybind11/stl.h> | ||
3 | #include <kms++/kms++.h> | ||
4 | #include <kms++util/kms++util.h> | ||
5 | |||
6 | namespace py = pybind11; | ||
7 | |||
8 | using namespace kms; | ||
9 | using namespace std; | ||
10 | |||
11 | void init_pykmstest(py::module &m) | ||
12 | { | ||
13 | py::class_<RGB>(m, "RGB") | ||
14 | .def(py::init<>()) | ||
15 | .def(py::init<uint8_t, uint8_t, uint8_t&>()) | ||
16 | .def(py::init<uint8_t, uint8_t, uint8_t, uint8_t&>()) | ||
17 | .def_property_readonly("rgb888", &RGB::rgb888) | ||
18 | .def_property_readonly("argb8888", &RGB::argb8888) | ||
19 | .def_property_readonly("abgr8888", &RGB::abgr8888) | ||
20 | .def_property_readonly("rgb565", &RGB::rgb565) | ||
21 | ; | ||
22 | |||
23 | py::class_<ResourceManager>(m, "ResourceManager") | ||
24 | .def(py::init<Card&>()) | ||
25 | .def("reset", &ResourceManager::reset) | ||
26 | .def("reserve_connector", (Connector* (ResourceManager::*)(const string& name))&ResourceManager::reserve_connector, | ||
27 | py::arg("name") = string()) | ||
28 | .def("reserve_crtc", (Crtc* (ResourceManager::*)(Connector*))&ResourceManager::reserve_crtc) | ||
29 | .def("reserve_plane", (Plane* (ResourceManager::*)(Crtc*, PlaneType, PixelFormat))&ResourceManager::reserve_plane, | ||
30 | py::arg("crtc"), | ||
31 | py::arg("type"), | ||
32 | py::arg("format") = PixelFormat::Undefined) | ||
33 | .def("reserve_generic_plane", &ResourceManager::reserve_generic_plane, | ||
34 | py::arg("crtc"), | ||
35 | py::arg("format") = PixelFormat::Undefined) | ||
36 | .def("reserve_primary_plane", &ResourceManager::reserve_primary_plane, | ||
37 | py::arg("crtc"), | ||
38 | py::arg("format") = PixelFormat::Undefined) | ||
39 | .def("reserve_overlay_plane", &ResourceManager::reserve_overlay_plane, | ||
40 | py::arg("crtc"), | ||
41 | py::arg("format") = PixelFormat::Undefined) | ||
42 | ; | ||
43 | py::enum_<YUVType>(m, "YUVType") | ||
44 | .value("BT601_Lim", YUVType::BT601_Lim) | ||
45 | .value("BT601_Full", YUVType::BT601_Full) | ||
46 | .value("BT709_Lim", YUVType::BT709_Lim) | ||
47 | .value("BT709_Full", YUVType::BT709_Full) | ||
48 | ; | ||
49 | |||
50 | // Use lambdas to handle IFramebuffer | ||
51 | m.def("draw_test_pattern", [](Framebuffer& fb, YUVType yuvt) { draw_test_pattern(fb, yuvt); }, | ||
52 | py::arg("fb"), | ||
53 | py::arg("yuvt") = YUVType::BT601_Lim); | ||
54 | m.def("draw_color_bar", [](Framebuffer& fb, int old_xpos, int xpos, int width) { | ||
55 | draw_color_bar(fb, old_xpos, xpos, width); | ||
56 | } ); | ||
57 | m.def("draw_rect", [](Framebuffer& fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color) { | ||
58 | draw_rect(fb, x, y, w, h, color); | ||
59 | } ); | ||
60 | m.def("draw_text", [](Framebuffer& fb, uint32_t x, uint32_t y, const string& str, RGB color) { | ||
61 | draw_text(fb, x, y, str, color); } ); | ||
62 | } | ||
diff --git a/py/pykms/pyvid.cpp b/py/pykms/pyvid.cpp new file mode 100644 index 0000000..92006c4 --- /dev/null +++ b/py/pykms/pyvid.cpp | |||
@@ -0,0 +1,39 @@ | |||
1 | #include <pybind11/pybind11.h> | ||
2 | #include <pybind11/stl.h> | ||
3 | #include <kms++/kms++.h> | ||
4 | #include <kms++util/kms++util.h> | ||
5 | #include <kms++util/videodevice.h> | ||
6 | |||
7 | namespace py = pybind11; | ||
8 | |||
9 | using namespace kms; | ||
10 | using namespace std; | ||
11 | |||
12 | void init_pyvid(py::module &m) | ||
13 | { | ||
14 | py::class_<VideoDevice>(m, "VideoDevice") | ||
15 | .def(py::init<const string&>()) | ||
16 | .def_property_readonly("fd", &VideoDevice::fd) | ||
17 | .def_property_readonly("has_capture", &VideoDevice::has_capture) | ||
18 | .def_property_readonly("has_output", &VideoDevice::has_output) | ||
19 | .def_property_readonly("has_m2m", &VideoDevice::has_m2m) | ||
20 | .def_property_readonly("capture_streamer", &VideoDevice::get_capture_streamer) | ||
21 | .def_property_readonly("output_streamer", &VideoDevice::get_output_streamer) | ||
22 | .def_property_readonly("discrete_frame_sizes", &VideoDevice::get_discrete_frame_sizes) | ||
23 | .def_property_readonly("frame_sizes", &VideoDevice::get_frame_sizes) | ||
24 | .def("get_capture_devices", &VideoDevice::get_capture_devices) | ||
25 | ; | ||
26 | |||
27 | py::class_<VideoStreamer>(m, "VideoStreamer") | ||
28 | .def_property_readonly("fd", &VideoStreamer::fd) | ||
29 | .def_property_readonly("ports", &VideoStreamer::get_ports) | ||
30 | .def("set_port", &VideoStreamer::set_port) | ||
31 | .def_property_readonly("formats", &VideoStreamer::get_formats) | ||
32 | .def("set_format", &VideoStreamer::set_format) | ||
33 | .def("set_queue_size", &VideoStreamer::set_queue_size) | ||
34 | .def("queue", &VideoStreamer::queue) | ||
35 | .def("dequeue", &VideoStreamer::dequeue) | ||
36 | .def("stream_on", &VideoStreamer::stream_on) | ||
37 | .def("stream_off", &VideoStreamer::stream_off) | ||
38 | ; | ||
39 | } | ||
diff --git a/py/tests/CMakeLists.txt b/py/tests/CMakeLists.txt new file mode 100644 index 0000000..a670ed9 --- /dev/null +++ b/py/tests/CMakeLists.txt | |||
@@ -0,0 +1,7 @@ | |||
1 | file(GLOB PY_SRCS "*.py") | ||
2 | add_custom_target(pyextras SOURCES ${PY_SRCS}) | ||
3 | |||
4 | add_test(NAME pytest COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/functest.py") | ||
5 | set_property(TEST pytest PROPERTY | ||
6 | ENVIRONMENT "PYTHONPATH=." "LD_LIBRARY_PATH=." | ||
7 | ) | ||
diff --git a/py/tests/alpha-test.py b/py/tests/alpha-test.py new file mode 100755 index 0000000..9ae1539 --- /dev/null +++ b/py/tests/alpha-test.py | |||
@@ -0,0 +1,62 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import time | ||
5 | |||
6 | card = pykms.Card() | ||
7 | res = pykms.ResourceManager(card) | ||
8 | conn = res.reserve_connector() | ||
9 | crtc = res.reserve_crtc(conn) | ||
10 | mode = conn.get_default_mode() | ||
11 | |||
12 | planes = [] | ||
13 | |||
14 | for i in range(3): | ||
15 | p = res.reserve_generic_plane(crtc) | ||
16 | |||
17 | if p == None: | ||
18 | print("Need 3 planes!") | ||
19 | exit(1) | ||
20 | |||
21 | planes.append(p) | ||
22 | |||
23 | card.disable_planes() | ||
24 | |||
25 | w = mode.hdisplay | ||
26 | h = mode.vdisplay | ||
27 | |||
28 | fbs=[] | ||
29 | |||
30 | for i in range(len(planes)): | ||
31 | fbs.append(pykms.DumbFramebuffer(card, w, h, "AR24")) | ||
32 | |||
33 | pykms.draw_rect(fbs[0], 50, 50, 200, 200, pykms.RGB(128, 255, 0, 0)) | ||
34 | pykms.draw_rect(fbs[1], 150, 50, 200, 200, pykms.RGB(128, 0, 255, 0)) | ||
35 | pykms.draw_rect(fbs[2], 50, 150, 200, 200, pykms.RGB(128, 0, 0, 255)) | ||
36 | |||
37 | crtc.set_props({ | ||
38 | "trans-key-mode": 0, | ||
39 | "trans-key": 0, | ||
40 | "background": 0, | ||
41 | "alpha_blender": 1, | ||
42 | }) | ||
43 | |||
44 | for i in range(len(planes)): | ||
45 | plane = planes[i] | ||
46 | fb = fbs[i] | ||
47 | |||
48 | print("set crtc {}, plane {}, fb {}".format(crtc.id, p.id, fbs[i].id)) | ||
49 | |||
50 | plane.set_props({ | ||
51 | "FB_ID": fb.id, | ||
52 | "CRTC_ID": crtc.id, | ||
53 | "SRC_W": fb.width << 16, | ||
54 | "SRC_H": fb.height << 16, | ||
55 | "CRTC_W": fb.width, | ||
56 | "CRTC_H": fb.height, | ||
57 | "zorder": i, | ||
58 | }) | ||
59 | |||
60 | time.sleep(1) | ||
61 | |||
62 | input("press enter to exit\n") | ||
diff --git a/py/tests/big_fb.py b/py/tests/big_fb.py new file mode 100755 index 0000000..54de685 --- /dev/null +++ b/py/tests/big_fb.py | |||
@@ -0,0 +1,279 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import random | ||
5 | import time | ||
6 | import sys | ||
7 | import select | ||
8 | import argparse | ||
9 | import selectors | ||
10 | |||
11 | black = pykms.RGB(0, 0, 0) | ||
12 | |||
13 | parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) | ||
14 | parser.add_argument('--flipmode', choices=['single', 'separate'], default='single', required=False, | ||
15 | help="""Page flip method to use: | ||
16 | single: Page flip on all displays with one request (default) | ||
17 | separate: Separate page flip on the displays""") | ||
18 | |||
19 | args = parser.parse_args() | ||
20 | |||
21 | card = pykms.Card() | ||
22 | |||
23 | if not card.has_atomic: | ||
24 | print('Atomic mode settings is not supported :(') | ||
25 | sys.exit() | ||
26 | |||
27 | if args.flipmode == 'single': | ||
28 | print('Page flip on all displays with one request') | ||
29 | elif args.flipmode == 'separate': | ||
30 | print('Page flip on all displays with separate requests') | ||
31 | |||
32 | res = pykms.ResourceManager(card) | ||
33 | |||
34 | conn_list = [] | ||
35 | crtc_list = [] | ||
36 | mode_list = [] | ||
37 | plane_list = [] | ||
38 | big_fb_list = [] | ||
39 | |||
40 | for conn in card.connectors: | ||
41 | if conn.connected() == 1: | ||
42 | conn_list.append(conn) | ||
43 | |||
44 | print('Have {} connected connectors:'.format(len(conn_list))) | ||
45 | for conn in conn_list: | ||
46 | crtc = res.reserve_crtc(conn) | ||
47 | crtc_list.append(crtc) | ||
48 | |||
49 | mode = conn.get_default_mode() | ||
50 | mode_list.append(mode) | ||
51 | |||
52 | print(' {}: {} ({}x{})'.format(conn.idx, conn.fullname, | ||
53 | mode.hdisplay, mode.vdisplay)) | ||
54 | |||
55 | fbX = sum(mode.hdisplay for mode in mode_list) | ||
56 | fbY = max(mode.vdisplay for mode in mode_list) | ||
57 | |||
58 | print('FB Resolution: {}x{}\n'.format(fbX, fbY)) | ||
59 | |||
60 | # Create the (big)framebuffer(s) | ||
61 | for x in range(2): | ||
62 | fb_tmp = pykms.DumbFramebuffer(card, fbX, fbY, 'XR24'); | ||
63 | big_fb_list.append(fb_tmp) | ||
64 | |||
65 | fb = big_fb_list[0] | ||
66 | screen_offset = 0 | ||
67 | |||
68 | card.disable_planes() | ||
69 | for i in range(0, len(conn_list)): | ||
70 | conn = conn_list[i] | ||
71 | crtc = crtc_list[i] | ||
72 | mode = mode_list[i] | ||
73 | |||
74 | plane = res.reserve_generic_plane(crtc) | ||
75 | plane_list.append(plane) | ||
76 | |||
77 | modeb = mode.to_blob(card) | ||
78 | req = pykms.AtomicReq(card) | ||
79 | req.add(conn, 'CRTC_ID', crtc.id) | ||
80 | req.add(crtc, {'ACTIVE': 1, | ||
81 | 'MODE_ID': modeb.id}) | ||
82 | req.add(plane, {'FB_ID': fb.id, | ||
83 | 'CRTC_ID': crtc.id, | ||
84 | 'SRC_X': screen_offset << 16, | ||
85 | 'SRC_Y': 0 << 16, | ||
86 | 'SRC_W': mode.hdisplay << 16, | ||
87 | 'SRC_H': mode.vdisplay << 16, | ||
88 | 'CRTC_X': 0, | ||
89 | 'CRTC_Y': 0, | ||
90 | 'CRTC_W': mode.hdisplay, | ||
91 | 'CRTC_H': mode.vdisplay, | ||
92 | 'zorder': 0}) | ||
93 | |||
94 | req.commit_sync(allow_modeset = True) | ||
95 | |||
96 | screen_offset += mode.hdisplay | ||
97 | |||
98 | # Double buffering, page flipping | ||
99 | class bigFB_db: | ||
100 | def __init__(self, fb1, fb2): | ||
101 | self.speed_y = random.randrange(1, 10, 1) | ||
102 | self.dir_y = random.randrange(-1, 3, 2) | ||
103 | self.first_run = True | ||
104 | self.fbs = [fb1,fb2] | ||
105 | self.draw_buf = 0 | ||
106 | self.fbX = fb1.width | ||
107 | self.fbY = fb1.height | ||
108 | self.pos_y = self.fbY // 2 | ||
109 | self.old_pos_y = -1 | ||
110 | # 5 + 10 + 15 + 10 + 5 = 45 | ||
111 | self.bar_size = 45 | ||
112 | self.flips = 0 | ||
113 | self.frames = 0 | ||
114 | self.time = 0 | ||
115 | self.flip_count = 100 | ||
116 | |||
117 | def new_color(self): | ||
118 | r = random.randrange(255) | ||
119 | g = random.randrange(255) | ||
120 | b = random.randrange(255) | ||
121 | self.color = pykms.RGB(r, g, b) | ||
122 | self.color2 = pykms.RGB(r // 2, g // 2, b // 2) | ||
123 | self.color3 = pykms.RGB(r // 3, g // 3, b // 3) | ||
124 | def move_stripe(self): | ||
125 | if self.first_run: | ||
126 | self.new_color() | ||
127 | self.first_run = False | ||
128 | |||
129 | fb = self.fbs[self.draw_buf] | ||
130 | |||
131 | old_box_y = self.old_pos_y | ||
132 | self.old_pos_y = self.pos_y | ||
133 | change_speed = 0 | ||
134 | |||
135 | self.pos_y = int(self.pos_y + (self.dir_y * self.speed_y)) | ||
136 | |||
137 | if self.pos_y < 0: | ||
138 | self.pos_y = 0 | ||
139 | change_speed = 1 | ||
140 | self.dir_y = 1 | ||
141 | elif self.pos_y > (self.fbY - self.bar_size): | ||
142 | self.pos_y = self.fbY - self.bar_size | ||
143 | change_speed = 1 | ||
144 | self.dir_y = -1 | ||
145 | |||
146 | if change_speed == 1: | ||
147 | self.new_color() | ||
148 | self.speed_y = random.randrange(1, 10, 1) | ||
149 | |||
150 | # Erease the old box | ||
151 | if old_box_y >= 0: | ||
152 | pykms.draw_rect(fb, 0, old_box_y, self.fbX, self.bar_size, black) | ||
153 | |||
154 | pos_y = self.pos_y | ||
155 | pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3) | ||
156 | pos_y += 5 | ||
157 | pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2) | ||
158 | pos_y += 10 | ||
159 | pykms.draw_rect(fb, 0, pos_y, self.fbX, 15, self.color) | ||
160 | pos_y += 15 | ||
161 | pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2) | ||
162 | pos_y += 10 | ||
163 | pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3) | ||
164 | |||
165 | def handle_page_flip_single(self): | ||
166 | self.draw_buf ^= 1 | ||
167 | self.move_stripe() | ||
168 | |||
169 | # one atomic request to flip on all displays/crtcs | ||
170 | fb = self.fbs[self.draw_buf] | ||
171 | screen_offset = 0 | ||
172 | |||
173 | req = pykms.AtomicReq(card) | ||
174 | for i in range(0, len(conn_list)): | ||
175 | crtc = crtc_list[i] | ||
176 | mode = mode_list[i] | ||
177 | |||
178 | plane = plane_list[i] | ||
179 | |||
180 | req.add(plane, {'FB_ID': fb.id, | ||
181 | 'CRTC_ID': crtc.id, | ||
182 | 'SRC_X': screen_offset << 16, | ||
183 | 'SRC_Y': 0 << 16, | ||
184 | 'SRC_W': mode.hdisplay << 16, | ||
185 | 'SRC_H': mode.vdisplay << 16, | ||
186 | 'CRTC_X': 0, | ||
187 | 'CRTC_Y': 0, | ||
188 | 'CRTC_W': mode.hdisplay, | ||
189 | 'CRTC_H': mode.vdisplay, | ||
190 | 'zorder': 0}) | ||
191 | |||
192 | screen_offset += mode.hdisplay | ||
193 | |||
194 | req.commit(0) | ||
195 | |||
196 | def handle_page_flip_separate(self): | ||
197 | self.draw_buf ^= 1 | ||
198 | self.move_stripe() | ||
199 | |||
200 | # ask to flip the first screen | ||
201 | fb = self.fbs[self.draw_buf] | ||
202 | screen_offset = 0 | ||
203 | |||
204 | # add separate atomic request for each display (crtc) | ||
205 | for i in range(0, len(conn_list)): | ||
206 | req = pykms.AtomicReq(card) | ||
207 | crtc = crtc_list[i] | ||
208 | mode = mode_list[i] | ||
209 | |||
210 | plane = plane_list[i] | ||
211 | |||
212 | req.add(plane, {'FB_ID': fb.id, | ||
213 | 'CRTC_ID': crtc.id, | ||
214 | 'SRC_X': screen_offset << 16, | ||
215 | 'SRC_Y': 0 << 16, | ||
216 | 'SRC_W': mode.hdisplay << 16, | ||
217 | 'SRC_H': mode.vdisplay << 16, | ||
218 | 'CRTC_X': 0, | ||
219 | 'CRTC_Y': 0, | ||
220 | 'CRTC_W': mode.hdisplay, | ||
221 | 'CRTC_H': mode.vdisplay, | ||
222 | 'zorder': 0}) | ||
223 | |||
224 | screen_offset += mode.hdisplay | ||
225 | |||
226 | req.commit(0) | ||
227 | |||
228 | def handle_page_flip_main(self, frame, time): | ||
229 | self.flip_count += 1 | ||
230 | |||
231 | if self.flip_count < len(conn_list): | ||
232 | return | ||
233 | |||
234 | self.flip_count = 0 | ||
235 | |||
236 | # statistics | ||
237 | self.flips += 1 | ||
238 | if self.time == 0: | ||
239 | self.frames = frame | ||
240 | self.time = time | ||
241 | |||
242 | time_delta = time - self.time | ||
243 | if time_delta >= 5: | ||
244 | frame_delta = frame - self.frames | ||
245 | print('Frame rate: %f (%u/%u frames in %f s)' % | ||
246 | (frame_delta / time_delta, self.flips, frame_delta, time_delta)) | ||
247 | |||
248 | self.flips = 0 | ||
249 | self.frames = frame | ||
250 | self.time = time | ||
251 | |||
252 | if args.flipmode == 'single': | ||
253 | self.handle_page_flip_single() | ||
254 | elif args.flipmode == 'separate': | ||
255 | self.handle_page_flip_separate() | ||
256 | |||
257 | print('Press ENTER to exit\n') | ||
258 | |||
259 | box_db = bigFB_db(big_fb_list[0], big_fb_list[1]) | ||
260 | box_db.handle_page_flip_main(0, 0) | ||
261 | |||
262 | def readdrm(fileobj, mask): | ||
263 | for ev in card.read_events(): | ||
264 | if ev.type == pykms.DrmEventType.FLIP_COMPLETE: | ||
265 | box_db.handle_page_flip_main(ev.seq, ev.time) | ||
266 | |||
267 | def readkey(fileobj, mask): | ||
268 | sys.stdin.readline() | ||
269 | exit(0) | ||
270 | |||
271 | sel = selectors.DefaultSelector() | ||
272 | sel.register(card.fd, selectors.EVENT_READ, readdrm) | ||
273 | sel.register(sys.stdin, selectors.EVENT_READ, readkey) | ||
274 | |||
275 | while True: | ||
276 | events = sel.select() | ||
277 | for key, mask in events: | ||
278 | callback = key.data | ||
279 | callback(key.fileobj, mask) | ||
diff --git a/py/tests/cam.py b/py/tests/cam.py new file mode 100755 index 0000000..c813b2f --- /dev/null +++ b/py/tests/cam.py | |||
@@ -0,0 +1,78 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import selectors | ||
5 | import pykms | ||
6 | |||
7 | w = 640 | ||
8 | h = 480 | ||
9 | fmt = pykms.PixelFormat.YUYV | ||
10 | |||
11 | card = pykms.Card() | ||
12 | res = pykms.ResourceManager(card) | ||
13 | conn = res.reserve_connector() | ||
14 | crtc = res.reserve_crtc(conn) | ||
15 | plane = res.reserve_overlay_plane(crtc, fmt) | ||
16 | |||
17 | mode = conn.get_default_mode() | ||
18 | modeb = mode.to_blob(card) | ||
19 | |||
20 | req = pykms.AtomicReq(card) | ||
21 | req.add(conn, "CRTC_ID", crtc.id) | ||
22 | req.add(crtc, {"ACTIVE": 1, | ||
23 | "MODE_ID": modeb.id}) | ||
24 | req.commit_sync(allow_modeset = True) | ||
25 | |||
26 | NUM_BUFS = 5 | ||
27 | |||
28 | fbs = [] | ||
29 | for i in range(NUM_BUFS): | ||
30 | fb = pykms.DumbFramebuffer(card, w, h, fmt) | ||
31 | fbs.append(fb) | ||
32 | |||
33 | vidpath = pykms.VideoDevice.get_capture_devices()[0] | ||
34 | |||
35 | vid = pykms.VideoDevice(vidpath) | ||
36 | cap = vid.capture_streamer | ||
37 | cap.set_port(0) | ||
38 | cap.set_format(fmt, w, h) | ||
39 | cap.set_queue_size(NUM_BUFS) | ||
40 | |||
41 | for fb in fbs: | ||
42 | cap.queue(fb) | ||
43 | |||
44 | cap.stream_on() | ||
45 | |||
46 | |||
47 | def readvid(conn, mask): | ||
48 | fb = cap.dequeue() | ||
49 | |||
50 | if card.has_atomic: | ||
51 | plane.set_props({ | ||
52 | "FB_ID": fb.id, | ||
53 | "CRTC_ID": crtc.id, | ||
54 | "SRC_W": fb.width << 16, | ||
55 | "SRC_H": fb.height << 16, | ||
56 | "CRTC_W": fb.width, | ||
57 | "CRTC_H": fb.height, | ||
58 | }) | ||
59 | else: | ||
60 | crtc.set_plane(plane, fb, 0, 0, fb.width, fb.height, | ||
61 | 0, 0, fb.width, fb.height) | ||
62 | |||
63 | cap.queue(fb) | ||
64 | |||
65 | def readkey(conn, mask): | ||
66 | #print("KEY EVENT"); | ||
67 | sys.stdin.readline() | ||
68 | exit(0) | ||
69 | |||
70 | sel = selectors.DefaultSelector() | ||
71 | sel.register(cap.fd, selectors.EVENT_READ, readvid) | ||
72 | sel.register(sys.stdin, selectors.EVENT_READ, readkey) | ||
73 | |||
74 | while True: | ||
75 | events = sel.select() | ||
76 | for key, mask in events: | ||
77 | callback = key.data | ||
78 | callback(key.fileobj, mask) | ||
diff --git a/py/tests/ctm_test.py b/py/tests/ctm_test.py new file mode 100755 index 0000000..7ceed6f --- /dev/null +++ b/py/tests/ctm_test.py | |||
@@ -0,0 +1,86 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import pykms | ||
5 | |||
6 | def ctm_to_blob(ctm, card): | ||
7 | len=9 | ||
8 | arr = bytearray(len*8) | ||
9 | view = memoryview(arr).cast("I") | ||
10 | |||
11 | for x in range(len): | ||
12 | i, d = divmod(ctm[x], 1) | ||
13 | if i < 0: | ||
14 | i = -i | ||
15 | sign = 1 << 31 | ||
16 | else: | ||
17 | sign = 0 | ||
18 | view[x * 2 + 0] = int(d * ((2 ** 32) - 1)) | ||
19 | view[x * 2 + 1] = int(i) | sign | ||
20 | #print("%f = %08x.%08x" % (ctm[x], view[x * 2 + 1], view[x * 2 + 0])) | ||
21 | |||
22 | return pykms.Blob(card, arr); | ||
23 | |||
24 | |||
25 | if len(sys.argv) > 1: | ||
26 | conn_name = sys.argv[1] | ||
27 | else: | ||
28 | conn_name = "" | ||
29 | |||
30 | card = pykms.Card() | ||
31 | res = pykms.ResourceManager(card) | ||
32 | conn = res.reserve_connector(conn_name) | ||
33 | crtc = res.reserve_crtc(conn) | ||
34 | mode = conn.get_default_mode() | ||
35 | |||
36 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
37 | pykms.draw_test_pattern(fb); | ||
38 | |||
39 | crtc.set_mode(conn, fb, mode) | ||
40 | |||
41 | input("press enter to set normal ctm\n") | ||
42 | |||
43 | ctm = [ 1.0, 0.0, 0.0, | ||
44 | 0.0, 1.0, 0.0, | ||
45 | 0.0, 0.0, 1.0 ] | ||
46 | |||
47 | ctmb = ctm_to_blob(ctm, card) | ||
48 | |||
49 | crtc.set_prop("CTM", ctmb.id) | ||
50 | |||
51 | input("press enter to set new ctm\n") | ||
52 | |||
53 | ctm = [ 0.0, 1.0, 0.0, | ||
54 | 0.0, 0.0, 1.0, | ||
55 | 1.0, 0.0, 0.0 ] | ||
56 | |||
57 | ctmb = ctm_to_blob(ctm, card) | ||
58 | |||
59 | crtc.set_prop("CTM", ctmb.id) | ||
60 | |||
61 | print("r->b g->r b->g ctm active\n") | ||
62 | |||
63 | input("press enter to set new ctm\n") | ||
64 | |||
65 | ctm = [ 0.0, 0.0, 1.0, | ||
66 | 1.0, 0.0, 0.0, | ||
67 | 0.0, 1.0, 0.0 ] | ||
68 | |||
69 | ctmb = ctm_to_blob(ctm, card) | ||
70 | |||
71 | crtc.set_prop("CTM", ctmb.id) | ||
72 | input("r->g g->b b->r ctm active\n") | ||
73 | |||
74 | input("press enter to turn off the crtc\n") | ||
75 | |||
76 | crtc.disable_mode() | ||
77 | |||
78 | input("press enter to enable crtc again\n") | ||
79 | |||
80 | crtc.set_mode(conn, fb, mode) | ||
81 | |||
82 | input("press enter to remove ctm\n") | ||
83 | |||
84 | crtc.set_prop("CTM", 0) | ||
85 | |||
86 | input("press enter to exit\n") | ||
diff --git a/py/tests/db.py b/py/tests/db.py new file mode 100755 index 0000000..f7b13eb --- /dev/null +++ b/py/tests/db.py | |||
@@ -0,0 +1,96 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import pykms | ||
5 | import selectors | ||
6 | |||
7 | bar_width = 20 | ||
8 | bar_speed = 8 | ||
9 | |||
10 | class FlipHandler(): | ||
11 | def __init__(self): | ||
12 | super().__init__() | ||
13 | self.bar_xpos = 0 | ||
14 | self.front_buf = 0 | ||
15 | self.fb1 = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
16 | self.fb2 = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
17 | self.flips = 0 | ||
18 | self.frames = 0 | ||
19 | self.time = 0 | ||
20 | |||
21 | def handle_page_flip(self, frame, time): | ||
22 | self.flips += 1 | ||
23 | if self.time == 0: | ||
24 | self.frames = frame | ||
25 | self.time = time | ||
26 | |||
27 | time_delta = time - self.time | ||
28 | if time_delta >= 5: | ||
29 | frame_delta = frame - self.frames | ||
30 | print("Frame rate: %f (%u/%u frames in %f s)" % | ||
31 | (frame_delta / time_delta, self.flips, frame_delta, time_delta)) | ||
32 | |||
33 | self.flips = 0 | ||
34 | self.frames = frame | ||
35 | self.time = time | ||
36 | |||
37 | if self.front_buf == 0: | ||
38 | fb = self.fb2 | ||
39 | else: | ||
40 | fb = self.fb1 | ||
41 | |||
42 | self.front_buf = self.front_buf ^ 1 | ||
43 | |||
44 | current_xpos = self.bar_xpos; | ||
45 | old_xpos = (current_xpos + (fb.width - bar_width - bar_speed)) % (fb.width - bar_width); | ||
46 | new_xpos = (current_xpos + bar_speed) % (fb.width - bar_width); | ||
47 | |||
48 | self.bar_xpos = new_xpos | ||
49 | |||
50 | pykms.draw_color_bar(fb, old_xpos, new_xpos, bar_width) | ||
51 | |||
52 | if card.has_atomic: | ||
53 | ctx = pykms.AtomicReq(card) | ||
54 | ctx.add(crtc.primary_plane, "FB_ID", fb.id) | ||
55 | ctx.commit() | ||
56 | else: | ||
57 | crtc.page_flip(fb) | ||
58 | |||
59 | if len(sys.argv) > 1: | ||
60 | conn_name = sys.argv[1] | ||
61 | else: | ||
62 | conn_name = '' | ||
63 | |||
64 | card = pykms.Card() | ||
65 | res = pykms.ResourceManager(card) | ||
66 | conn = res.reserve_connector(conn_name) | ||
67 | crtc = res.reserve_crtc(conn) | ||
68 | mode = conn.get_default_mode() | ||
69 | |||
70 | fliphandler = FlipHandler() | ||
71 | |||
72 | crtc.set_mode(conn, fliphandler.fb1, mode) | ||
73 | |||
74 | fliphandler.handle_page_flip(0, 0) | ||
75 | |||
76 | def readdrm(fileobj, mask): | ||
77 | #print("EVENT"); | ||
78 | for ev in card.read_events(): | ||
79 | if ev.type == pykms.DrmEventType.FLIP_COMPLETE: | ||
80 | fliphandler.handle_page_flip(ev.seq, ev.time) | ||
81 | |||
82 | |||
83 | def readkey(fileobj, mask): | ||
84 | #print("KEY EVENT"); | ||
85 | sys.stdin.readline() | ||
86 | exit(0) | ||
87 | |||
88 | sel = selectors.DefaultSelector() | ||
89 | sel.register(card.fd, selectors.EVENT_READ, readdrm) | ||
90 | sel.register(sys.stdin, selectors.EVENT_READ, readkey) | ||
91 | |||
92 | while True: | ||
93 | events = sel.select() | ||
94 | for key, mask in events: | ||
95 | callback = key.data | ||
96 | callback(key.fileobj, mask) | ||
diff --git a/py/tests/functest.py b/py/tests/functest.py new file mode 100755 index 0000000..836b880 --- /dev/null +++ b/py/tests/functest.py | |||
@@ -0,0 +1,18 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | |||
5 | card = pykms.Card() | ||
6 | res = pykms.ResourceManager(card) | ||
7 | conn = res.reserve_connector() | ||
8 | crtc = res.reserve_crtc(conn) | ||
9 | |||
10 | mode = conn.get_default_mode() | ||
11 | |||
12 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
13 | pykms.draw_test_pattern(fb); | ||
14 | |||
15 | crtc.set_mode(conn, fb, mode) | ||
16 | |||
17 | print("OK") | ||
18 | |||
diff --git a/py/tests/gamma.py b/py/tests/gamma.py new file mode 100755 index 0000000..5969b82 --- /dev/null +++ b/py/tests/gamma.py | |||
@@ -0,0 +1,40 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | |||
5 | # This hack makes drm initialize the fbcon, setting up the default connector | ||
6 | card = pykms.Card() | ||
7 | card = 0 | ||
8 | |||
9 | card = pykms.Card() | ||
10 | res = pykms.ResourceManager(card) | ||
11 | conn = res.reserve_connector() | ||
12 | crtc = res.reserve_crtc(conn) | ||
13 | mode = conn.get_default_mode() | ||
14 | |||
15 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
16 | pykms.draw_test_pattern(fb); | ||
17 | |||
18 | crtc.set_mode(conn, fb, mode) | ||
19 | |||
20 | len=256 | ||
21 | arr = bytearray(len*2*4) | ||
22 | view = memoryview(arr).cast("H") | ||
23 | |||
24 | for i in range(len): | ||
25 | g = round(65535 * pow(i / float(len), 1 / 2.2)) | ||
26 | |||
27 | view[i * 4 + 0] = g | ||
28 | view[i * 4 + 1] = g | ||
29 | view[i * 4 + 2] = g | ||
30 | view[i * 4 + 3] = 0 | ||
31 | |||
32 | gamma = pykms.Blob(card, arr); | ||
33 | |||
34 | crtc.set_prop("GAMMA_LUT", gamma.id) | ||
35 | |||
36 | input("press enter to remove gamma\n") | ||
37 | |||
38 | crtc.set_prop("GAMMA_LUT", 0) | ||
39 | |||
40 | input("press enter to exit\n") | ||
diff --git a/py/tests/hpd.py b/py/tests/hpd.py new file mode 100755 index 0000000..d26f260 --- /dev/null +++ b/py/tests/hpd.py | |||
@@ -0,0 +1,22 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pyudev | ||
4 | import pykms | ||
5 | |||
6 | card = pykms.Card() | ||
7 | res = pykms.ResourceManager(card) | ||
8 | conn = res.reserve_connector("hdmi") | ||
9 | |||
10 | context = pyudev.Context() | ||
11 | |||
12 | dev = pyudev.Devices.from_name(context, 'drm', 'card0') | ||
13 | |||
14 | monitor = pyudev.Monitor.from_netlink(context) | ||
15 | monitor.filter_by('drm') | ||
16 | |||
17 | for device in iter(monitor.poll, None): | ||
18 | if 'HOTPLUG' in device: | ||
19 | conn.refresh() | ||
20 | mode = conn.get_modes() | ||
21 | print("HPD") | ||
22 | print(mode) | ||
diff --git a/py/tests/iact.py b/py/tests/iact.py new file mode 100755 index 0000000..721e558 --- /dev/null +++ b/py/tests/iact.py | |||
@@ -0,0 +1,42 @@ | |||
1 | #!/usr/bin/python3 -i | ||
2 | |||
3 | # This is a base script for interactive kms++ python environment | ||
4 | |||
5 | import pykms | ||
6 | from time import sleep | ||
7 | from math import sin | ||
8 | from math import cos | ||
9 | |||
10 | card = pykms.Card() | ||
11 | res = pykms.ResourceManager(card) | ||
12 | conn = res.reserve_connector() | ||
13 | crtc = res.reserve_crtc(conn) | ||
14 | |||
15 | mode = conn.get_default_mode() | ||
16 | |||
17 | fb = pykms.DumbFramebuffer(card, 200, 200, "XR24"); | ||
18 | pykms.draw_test_pattern(fb); | ||
19 | |||
20 | #crtc.set_mode(conn, fb, mode) | ||
21 | |||
22 | i = 0 | ||
23 | for p in card.planes: | ||
24 | globals()["plane"+str(i)] = p | ||
25 | i=i+1 | ||
26 | |||
27 | i = 0 | ||
28 | for c in card.crtcs: | ||
29 | globals()["crtc"+str(i)] = c | ||
30 | i=i+1 | ||
31 | |||
32 | for p in crtc.possible_planes: | ||
33 | if p.plane_type == pykms.PlaneType.Overlay: | ||
34 | plane = p | ||
35 | break | ||
36 | |||
37 | def set_plane(x, y): | ||
38 | crtc.set_plane(plane, fb, x, y, fb.width, fb.height, 0, 0, fb.width, fb.height) | ||
39 | |||
40 | set_plane(0, 0) | ||
41 | |||
42 | # for x in range(0, crtc.width() - fb.width()): set_plane(x, int((sin(x/50) + 1) * 100)); sleep(0.01) | ||
diff --git a/py/tests/kmsmodeview.py b/py/tests/kmsmodeview.py new file mode 100755 index 0000000..355db02 --- /dev/null +++ b/py/tests/kmsmodeview.py | |||
@@ -0,0 +1,317 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import urwid | ||
4 | import pykms | ||
5 | |||
6 | def exit_on_q(key): | ||
7 | if key in ('q', 'Q'): | ||
8 | raise urwid.ExitMainLoop() | ||
9 | elif key == 'a': | ||
10 | apply_mode() | ||
11 | |||
12 | alarm_handle = None | ||
13 | |||
14 | def recalc_info(l, d): | ||
15 | global alarm_handle | ||
16 | |||
17 | alarm_handle = None | ||
18 | |||
19 | for w in recalc_list: | ||
20 | w.recalc() | ||
21 | |||
22 | def div_or_zero(n, d): | ||
23 | if d == 0: | ||
24 | return 0 | ||
25 | else: | ||
26 | return n / d | ||
27 | |||
28 | class MyIntEdit(urwid.IntEdit): | ||
29 | _metaclass_ = urwid.signals.MetaSignals | ||
30 | signals = ['value_change'] | ||
31 | |||
32 | def __init__(self, caption, calc=None): | ||
33 | self._myval = 0 | ||
34 | self._disable_change = False | ||
35 | self._calc = calc | ||
36 | self._updlist = None | ||
37 | |||
38 | super().__init__(caption, 0) | ||
39 | |||
40 | def set_edit_text(self, text): | ||
41 | global alarm_handle | ||
42 | |||
43 | super().set_edit_text(text) | ||
44 | newtext = super().get_edit_text() | ||
45 | new_val = int(newtext) if newtext != "" else 0 | ||
46 | if new_val != self._myval: | ||
47 | self._myval = new_val | ||
48 | if not self._disable_change: | ||
49 | urwid.emit_signal(self, 'value_change', self, self._myval) | ||
50 | |||
51 | if alarm_handle == None: | ||
52 | alarm_handle = loop.set_alarm_in(0, recalc_info) | ||
53 | |||
54 | if self._updlist != None: | ||
55 | for w in self._updlist: | ||
56 | w.recalc() | ||
57 | |||
58 | def recalc(self): | ||
59 | self._disable_change = True | ||
60 | self.set_val(self._calc()) | ||
61 | self._disable_change = False | ||
62 | |||
63 | def set_val(self, val): | ||
64 | self.set_edit_text(str(int(val))) | ||
65 | |||
66 | def get_val(self): | ||
67 | return self._myval | ||
68 | |||
69 | def set_updlist(self, list): | ||
70 | self._updlist = list | ||
71 | |||
72 | def keypress(self, size, key): | ||
73 | if key == '+': | ||
74 | self.set_edit_text(str(self.value() + 1)) | ||
75 | elif key == '-': | ||
76 | self.set_edit_text(str(self.value() - 1)) | ||
77 | else: | ||
78 | return super().keypress(size, key) | ||
79 | |||
80 | class MyIntText(urwid.Text): | ||
81 | def __init__(self, fmt, calc=None): | ||
82 | super().__init__("") | ||
83 | self._fmt = fmt | ||
84 | self._calc = calc | ||
85 | |||
86 | def recalc(self): | ||
87 | val = self._calc() | ||
88 | super().set_text(self._fmt.format(val)) | ||
89 | |||
90 | def khz_to_ps(khz): | ||
91 | if khz == 0: | ||
92 | return 0 | ||
93 | else: | ||
94 | return 1.0 / khz * 1000 * 1000 * 1000 | ||
95 | |||
96 | def khz_to_us(khz): | ||
97 | if khz == 0: | ||
98 | return 0 | ||
99 | else: | ||
100 | return 1.0 / khz * 1000 | ||
101 | |||
102 | pclk_khz_widget = MyIntEdit(u"pclk (kHz) ") | ||
103 | pclk_ps_widget = MyIntText(fmt="pclk {:.2f} ps", calc = lambda: khz_to_ps(pclk_khz_widget.get_val())) | ||
104 | |||
105 | pclk_widgets = [pclk_khz_widget, pclk_ps_widget] | ||
106 | |||
107 | pclk_columns = urwid.LineBox(urwid.Columns(pclk_widgets), title = "Pixel clock") | ||
108 | |||
109 | # Horizontal widgets | ||
110 | |||
111 | hdisp_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp2_widget.get_val()) | ||
112 | hfp_widget = MyIntEdit(u"hfp ", calc = lambda: hss_widget.get_val() - hdisp_widget.get_val()) | ||
113 | hsw_widget = MyIntEdit(u"hsw ", calc = lambda: hse_widget.get_val() - hss_widget.get_val()) | ||
114 | hbp_widget = MyIntEdit(u"hbp ", calc = lambda: htot_widget.get_val() - hse_widget.get_val()) | ||
115 | |||
116 | hdisp2_widget = MyIntEdit(u"hdisp ", calc = lambda: hdisp_widget.get_val()) | ||
117 | hss_widget = MyIntEdit(u"hss ", | ||
118 | calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val()) | ||
119 | hse_widget = MyIntEdit(u"hse ", | ||
120 | calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val()) | ||
121 | htot_widget = MyIntEdit(u"htot ", | ||
122 | calc = lambda: hdisp_widget.get_val() + hfp_widget.get_val() + hsw_widget.get_val() + hbp_widget.get_val()) | ||
123 | |||
124 | hwidgets1 = [hdisp_widget, hfp_widget, hsw_widget, hbp_widget] | ||
125 | hwidgets2 = [hdisp2_widget, hss_widget, hse_widget, htot_widget] | ||
126 | |||
127 | horiz_pile1 = urwid.Pile(hwidgets1) | ||
128 | horiz_pile2 = urwid.Pile(hwidgets2) | ||
129 | |||
130 | h_columns = urwid.LineBox(urwid.Columns([(15, horiz_pile1), (15, horiz_pile2)]), title = "Horizontal") | ||
131 | |||
132 | # Vertical columns | ||
133 | |||
134 | vdisp_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp2_widget.get_val()) | ||
135 | vfp_widget = MyIntEdit(u"vfp ", calc = lambda: vss_widget.get_val() - vdisp_widget.get_val()) | ||
136 | vsw_widget = MyIntEdit(u"vsw ", calc = lambda: vse_widget.get_val() - vss_widget.get_val()) | ||
137 | vbp_widget = MyIntEdit(u"vbp ", calc = lambda: vtot_widget.get_val() - vse_widget.get_val()) | ||
138 | |||
139 | vdisp2_widget = MyIntEdit(u"vdisp ", calc = lambda: vdisp_widget.get_val()) | ||
140 | vss_widget = MyIntEdit(u"vss ", | ||
141 | calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val()) | ||
142 | vse_widget = MyIntEdit(u"vse ", | ||
143 | calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val()) | ||
144 | vtot_widget = MyIntEdit(u"vtot ", | ||
145 | calc = lambda: vdisp_widget.get_val() + vfp_widget.get_val() + vsw_widget.get_val() + vbp_widget.get_val()) | ||
146 | |||
147 | vwidgets1 = [vdisp_widget, vfp_widget, vsw_widget, vbp_widget] | ||
148 | vwidgets2 = [vdisp2_widget, vss_widget, vse_widget, vtot_widget] | ||
149 | |||
150 | vert_pile1 = urwid.Pile(vwidgets1) | ||
151 | vert_pile2 = urwid.Pile(vwidgets2) | ||
152 | |||
153 | v_columns = urwid.LineBox(urwid.Columns([(15, vert_pile1), (15, vert_pile2)]), title = "Vertical") | ||
154 | |||
155 | # Info widgets | ||
156 | |||
157 | line_us_widget = MyIntText(fmt="line {:.2f} us", | ||
158 | calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val()) | ||
159 | line_khz_widget = MyIntText(fmt="line {:.2f} kHz", | ||
160 | calc = lambda: div_or_zero(pclk_khz_widget.get_val(), htot_widget.get_val())) | ||
161 | |||
162 | frame_tot_widget = MyIntText(fmt="tot {} pix", | ||
163 | calc = lambda: htot_widget.get_val() * vtot_widget.get_val()) | ||
164 | frame_us_widget = MyIntText(fmt="frame {:.2f} ms", | ||
165 | calc = lambda: khz_to_us(pclk_khz_widget.get_val()) * htot_widget.get_val() * vtot_widget.get_val() / 1000) | ||
166 | frame_khz_widget = MyIntText(fmt="frame {:.2f} Hz", | ||
167 | calc = lambda: div_or_zero(pclk_khz_widget.get_val() * 1000, htot_widget.get_val() * vtot_widget.get_val())) | ||
168 | |||
169 | info_box = urwid.LineBox(urwid.Pile([line_us_widget, line_khz_widget, urwid.Divider(), frame_tot_widget, frame_us_widget, frame_khz_widget]), title = "Info") | ||
170 | |||
171 | # Set update lists | ||
172 | |||
173 | recalc_list = [ pclk_ps_widget, line_us_widget, line_khz_widget, frame_tot_widget, frame_us_widget, frame_khz_widget ] | ||
174 | |||
175 | hdisp_widget.set_updlist([hdisp2_widget, hss_widget, hse_widget, htot_widget]) | ||
176 | hfp_widget.set_updlist([hss_widget, hse_widget, htot_widget]) | ||
177 | hsw_widget.set_updlist([hse_widget, htot_widget]) | ||
178 | hbp_widget.set_updlist([htot_widget]) | ||
179 | hdisp2_widget.set_updlist([hdisp_widget, hfp_widget]) | ||
180 | hss_widget.set_updlist([hfp_widget, hsw_widget]) | ||
181 | hse_widget.set_updlist([hsw_widget, hbp_widget]) | ||
182 | htot_widget.set_updlist([hbp_widget]) | ||
183 | |||
184 | vdisp_widget.set_updlist([vdisp2_widget, vss_widget, vse_widget, vtot_widget]) | ||
185 | vfp_widget.set_updlist([vss_widget, vse_widget, vtot_widget]) | ||
186 | vsw_widget.set_updlist([vse_widget, vtot_widget]) | ||
187 | vbp_widget.set_updlist([vtot_widget]) | ||
188 | vdisp2_widget.set_updlist([vdisp_widget, vfp_widget]) | ||
189 | vss_widget.set_updlist([vfp_widget, vsw_widget]) | ||
190 | vse_widget.set_updlist([vsw_widget, vbp_widget]) | ||
191 | vtot_widget.set_updlist([vbp_widget]) | ||
192 | |||
193 | # Flags | ||
194 | |||
195 | fb = None | ||
196 | |||
197 | DRM_MODE_FLAG_PHSYNC = (1<<0) | ||
198 | DRM_MODE_FLAG_NHSYNC = (1<<1) | ||
199 | DRM_MODE_FLAG_PVSYNC = (1<<2) | ||
200 | DRM_MODE_FLAG_NVSYNC = (1<<3) | ||
201 | DRM_MODE_FLAG_INTERLACE = (1<<4) | ||
202 | DRM_MODE_FLAG_DBLCLK = (1<<12) | ||
203 | |||
204 | def mode_is_ilace(mode): | ||
205 | return (mode.flags & DRM_MODE_FLAG_INTERLACE) != 0 | ||
206 | |||
207 | def apply_mode(): | ||
208 | global fb | ||
209 | |||
210 | mode = pykms.Videomode() | ||
211 | mode.clock = pclk_khz_widget.get_val() | ||
212 | |||
213 | mode.hdisplay = hdisp2_widget.get_val() | ||
214 | mode.hsync_start = hss_widget.get_val() | ||
215 | mode.hsync_end = hse_widget.get_val() | ||
216 | mode.htotal = htot_widget.get_val() | ||
217 | |||
218 | mode.vdisplay = vdisp2_widget.get_val() | ||
219 | mode.vsync_start = vss_widget.get_val() | ||
220 | mode.vsync_end = vse_widget.get_val() | ||
221 | mode.vtotal = vtot_widget.get_val() | ||
222 | |||
223 | if ilace_box.state: | ||
224 | mode.flags |= DRM_MODE_FLAG_INTERLACE | ||
225 | |||
226 | if dblclk_box.state: | ||
227 | mode.flags |= DRM_MODE_FLAG_DBLCLK | ||
228 | |||
229 | if hsync_pol.state == True: | ||
230 | mode.flags |= DRM_MODE_FLAG_PHSYNC | ||
231 | elif hsync_pol.state == False: | ||
232 | mode.flags |= DRM_MODE_FLAG_NHSYNC | ||
233 | |||
234 | if vsync_pol.state == True: | ||
235 | mode.flags |= DRM_MODE_FLAG_PVSYNC | ||
236 | elif vsync_pol.state == False: | ||
237 | mode.flags |= DRM_MODE_FLAG_NVSYNC | ||
238 | |||
239 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
240 | pykms.draw_test_pattern(fb); | ||
241 | |||
242 | crtc.set_mode(conn, fb, mode) | ||
243 | |||
244 | def read_mode(mode): | ||
245 | pclk_khz_widget.set_val(mode.clock) | ||
246 | hdisp2_widget.set_val(mode.hdisplay) | ||
247 | hss_widget.set_val(mode.hsync_start) | ||
248 | hse_widget.set_val(mode.hsync_end) | ||
249 | htot_widget.set_val(mode.htotal) | ||
250 | |||
251 | vdisp2_widget.set_val(mode.vdisplay) | ||
252 | vss_widget.set_val(mode.vsync_start) | ||
253 | vse_widget.set_val(mode.vsync_end) | ||
254 | vtot_widget.set_val(mode.vtotal) | ||
255 | |||
256 | ilace_box.set_state(mode_is_ilace(mode)) | ||
257 | dblclk_box.set_state((mode.flags & DRM_MODE_FLAG_DBLCLK) != 0) | ||
258 | |||
259 | sync = 'mixed' | ||
260 | if (mode.flags & DRM_MODE_FLAG_PHSYNC) != 0: | ||
261 | sync = True | ||
262 | elif (mode.flags & DRM_MODE_FLAG_NHSYNC) != 0: | ||
263 | sync = False | ||
264 | hsync_pol.set_state(sync) | ||
265 | |||
266 | sync = 'mixed' | ||
267 | if (mode.flags & DRM_MODE_FLAG_PVSYNC) != 0: | ||
268 | sync = True | ||
269 | elif (mode.flags & DRM_MODE_FLAG_NVSYNC) != 0: | ||
270 | sync = False | ||
271 | vsync_pol.set_state(sync) | ||
272 | |||
273 | def apply_press(w): | ||
274 | apply_mode() | ||
275 | |||
276 | ilace_box = urwid.CheckBox('interlace') | ||
277 | hsync_pol = urwid.CheckBox('hsync positive', has_mixed=True) | ||
278 | vsync_pol = urwid.CheckBox('vsync positive', has_mixed=True) | ||
279 | dblclk_box = urwid.CheckBox('double clock') | ||
280 | |||
281 | flags_pile = urwid.LineBox(urwid.Pile([ilace_box, hsync_pol, vsync_pol, dblclk_box]), title = "Flags") | ||
282 | |||
283 | apply_button = urwid.LineBox(urwid.Padding(urwid.Button('apply', on_press=apply_press))) | ||
284 | |||
285 | # Main | ||
286 | |||
287 | def mode_press(w, mode): | ||
288 | read_mode(mode) | ||
289 | |||
290 | def mode_to_str(mode): | ||
291 | return "{}@{}{}".format(mode.name, mode.vrefresh, "i" if mode_is_ilace(mode) else "") | ||
292 | |||
293 | mode_buttons = [] | ||
294 | |||
295 | card = pykms.Card() | ||
296 | conn = card.get_first_connected_connector() | ||
297 | crtc = conn.get_current_crtc() | ||
298 | modes = conn.get_modes() | ||
299 | i = 0 | ||
300 | for m in modes: | ||
301 | mode_buttons.append(urwid.Button(mode_to_str(m), on_press=mode_press, user_data=m)) | ||
302 | i += 1 | ||
303 | |||
304 | modes_pile = urwid.LineBox(urwid.Pile(mode_buttons), title = "Video modes") | ||
305 | |||
306 | main_pile = urwid.Pile([modes_pile, pclk_columns, urwid.Columns([ h_columns, v_columns ]), info_box, flags_pile, apply_button]) | ||
307 | |||
308 | main_columns = urwid.Filler(main_pile, valign='top') | ||
309 | |||
310 | loop = urwid.MainLoop(main_columns, unhandled_input=exit_on_q, handle_mouse=False) | ||
311 | |||
312 | # select the first mode | ||
313 | mode_press(None, modes[0]) | ||
314 | |||
315 | loop.run() | ||
316 | |||
317 | fb = None | ||
diff --git a/py/tests/modeset_event.py b/py/tests/modeset_event.py new file mode 100755 index 0000000..11cfd58 --- /dev/null +++ b/py/tests/modeset_event.py | |||
@@ -0,0 +1,73 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import selectors | ||
5 | import sys | ||
6 | |||
7 | def readdrm(fileobj, mask): | ||
8 | for ev in card.read_events(): | ||
9 | eventhandler(ev) | ||
10 | |||
11 | def waitevent(sel): | ||
12 | events = sel.select(1) | ||
13 | if not events: | ||
14 | print("Error: timeout receiving event") | ||
15 | else: | ||
16 | for key, mask in events: | ||
17 | key.data(key.fileobj, mask) | ||
18 | |||
19 | def eventhandler(event): | ||
20 | print("Received %s event successfully (seq %d time %f)" % | ||
21 | (event.type, event.seq, event.time)) | ||
22 | |||
23 | card = pykms.Card() | ||
24 | sel = selectors.DefaultSelector() | ||
25 | sel.register(card.fd, selectors.EVENT_READ, readdrm) | ||
26 | |||
27 | res = pykms.ResourceManager(card) | ||
28 | conn = res.reserve_connector() | ||
29 | crtc = res.reserve_crtc(conn) | ||
30 | pplane = res.reserve_primary_plane(crtc) | ||
31 | |||
32 | mode = conn.get_default_mode() | ||
33 | modeb = mode.to_blob(card) | ||
34 | |||
35 | for format in pplane.formats: | ||
36 | if format == pykms.PixelFormat.XRGB8888: | ||
37 | break | ||
38 | if format == pykms.PixelFormat.RGB565: | ||
39 | break | ||
40 | |||
41 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, format); | ||
42 | pykms.draw_test_pattern(fb); | ||
43 | |||
44 | # Disable request | ||
45 | card.disable_planes() | ||
46 | |||
47 | print("Setting %s to %s using %s" % (conn.fullname, mode.name, format)) | ||
48 | |||
49 | req = pykms.AtomicReq(card) | ||
50 | |||
51 | req.add(conn, "CRTC_ID", crtc.id) | ||
52 | req.add(crtc, {"ACTIVE": 1, | ||
53 | "MODE_ID": modeb.id}) | ||
54 | req.add(pplane, {"FB_ID": fb.id, | ||
55 | "CRTC_ID": crtc.id, | ||
56 | "SRC_X": 0 << 16, | ||
57 | "SRC_Y": 0 << 16, | ||
58 | "SRC_W": mode.hdisplay << 16, | ||
59 | "SRC_H": mode.vdisplay << 16, | ||
60 | "CRTC_X": 0, | ||
61 | "CRTC_Y": 0, | ||
62 | "CRTC_W": mode.hdisplay, | ||
63 | "CRTC_H": mode.vdisplay}) | ||
64 | |||
65 | ret = req.test(True) | ||
66 | if ret != 0: | ||
67 | print("Atomic test failed: %d" % ret) | ||
68 | sys.exit() | ||
69 | |||
70 | req.commit(0, allow_modeset = True) | ||
71 | waitevent(sel) | ||
72 | |||
73 | input("press enter to exit\n") | ||
diff --git a/py/tests/plane_csc.py b/py/tests/plane_csc.py new file mode 100755 index 0000000..be92c00 --- /dev/null +++ b/py/tests/plane_csc.py | |||
@@ -0,0 +1,66 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | |||
5 | card = pykms.Card() | ||
6 | res = pykms.ResourceManager(card) | ||
7 | conn = res.reserve_connector("HDMI") | ||
8 | crtc = res.reserve_crtc(conn) | ||
9 | mode = conn.get_default_mode() | ||
10 | modeb = mode.to_blob(card) | ||
11 | plane = res.reserve_generic_plane(crtc, pykms.PixelFormat.UYVY) | ||
12 | #plane = res.reserve_generic_plane(crtc, pykms.PixelFormat.Undefined) | ||
13 | |||
14 | print("Got plane %d %d" % (plane.idx, plane.id)) | ||
15 | |||
16 | fb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "UYVY"); | ||
17 | pykms.draw_test_pattern(fb); | ||
18 | |||
19 | req = pykms.AtomicReq(card) | ||
20 | req.add(conn, "CRTC_ID", crtc.id) | ||
21 | req.add(crtc, {"ACTIVE": 1, | ||
22 | "MODE_ID": modeb.id}) | ||
23 | |||
24 | input("Press enter to enable crtc idx %d at %s" % (crtc.idx, conn.fullname)) | ||
25 | r = req.commit_sync(allow_modeset = True) | ||
26 | |||
27 | input("Press enter to enable plane idx %d at %s" % (plane.idx, conn.fullname)) | ||
28 | |||
29 | req = pykms.AtomicReq(card) | ||
30 | req.add(plane, {"FB_ID": fb.id, | ||
31 | "CRTC_ID": crtc.id, | ||
32 | "SRC_X": 0 << 16, | ||
33 | "SRC_Y": 0 << 16, | ||
34 | "SRC_W": fb.width << 16, | ||
35 | "SRC_H": fb.height << 16, | ||
36 | "CRTC_X": 0, | ||
37 | "CRTC_Y": 0, | ||
38 | "CRTC_W": fb.width, | ||
39 | "CRTC_H": fb.height, | ||
40 | "zorder": 0}) | ||
41 | r = req.commit_sync() | ||
42 | print("Plane enable request returned %d\n" % r) | ||
43 | |||
44 | yuv_types = [pykms.YUVType.BT601_Lim, | ||
45 | pykms.YUVType.BT601_Full, | ||
46 | pykms.YUVType.BT709_Lim, | ||
47 | pykms.YUVType.BT709_Full] | ||
48 | |||
49 | encoding_enums = plane.get_prop("COLOR_ENCODING").enums | ||
50 | range_enums = plane.get_prop("COLOR_RANGE").enums | ||
51 | |||
52 | for i in range(0, 2): | ||
53 | for j in range(0, 2): | ||
54 | input("press enter to for encoding: \"%s\" range: \"%s\"\n" % | ||
55 | (encoding_enums[i], range_enums[j])) | ||
56 | |||
57 | req = pykms.AtomicReq(card) | ||
58 | req.add(plane, {"COLOR_ENCODING": i, | ||
59 | "COLOR_RANGE": j}) | ||
60 | req.commit_sync() | ||
61 | |||
62 | for t in yuv_types: | ||
63 | input("press enter to redraw with yuv_type %s\n" % t) | ||
64 | pykms.draw_test_pattern(fb, t); | ||
65 | |||
66 | input("press enter to exit\n") | ||
diff --git a/py/tests/plane_hog.py b/py/tests/plane_hog.py new file mode 100755 index 0000000..5bdc937 --- /dev/null +++ b/py/tests/plane_hog.py | |||
@@ -0,0 +1,136 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import sys | ||
5 | |||
6 | card = pykms.Card() | ||
7 | res = pykms.ResourceManager(card) | ||
8 | |||
9 | conn1 = False | ||
10 | conn2 = False | ||
11 | |||
12 | for conn in card.connectors: | ||
13 | if not conn1: | ||
14 | conn1 = conn | ||
15 | elif not conn2: | ||
16 | conn2 = conn | ||
17 | else: | ||
18 | break | ||
19 | |||
20 | crtc1 = res.reserve_crtc(conn1) | ||
21 | mode1 = conn1.get_default_mode() | ||
22 | modeb1 = mode1.to_blob(card) | ||
23 | print("CRTC idx %d goes to %s connector" % (crtc1.idx, conn1.fullname)) | ||
24 | |||
25 | if conn2: | ||
26 | crtc2 = res.reserve_crtc(conn2) | ||
27 | mode2 = conn2.get_default_mode() | ||
28 | modeb2 = mode2.to_blob(card) | ||
29 | print("CRTC idx %d goes to %s connector" % (crtc2.idx, conn2.fullname)) | ||
30 | |||
31 | fbwidth = 480 | ||
32 | fbheight = 270 | ||
33 | |||
34 | fb = pykms.DumbFramebuffer(card, fbwidth, fbheight, "AR24"); | ||
35 | pykms.draw_test_pattern(fb); | ||
36 | |||
37 | # Disable request | ||
38 | card.disable_planes() | ||
39 | |||
40 | plane_list = [] | ||
41 | |||
42 | while True: | ||
43 | plane = res.reserve_generic_plane(crtc1) | ||
44 | if plane: | ||
45 | print("Got plane idx %d" % plane.idx) | ||
46 | plane_list.append(plane) | ||
47 | else: | ||
48 | break | ||
49 | |||
50 | print("Got %d planes" % len(plane_list)) | ||
51 | |||
52 | req = pykms.AtomicReq(card) | ||
53 | req.add(conn1, "CRTC_ID", crtc1.id) | ||
54 | req.add(crtc1, {"ACTIVE": 1, | ||
55 | "MODE_ID": modeb1.id}) | ||
56 | |||
57 | input("Press enter to enable crtc idx %d at %s" % (crtc1.idx, conn1.fullname)) | ||
58 | r = req.commit_sync(allow_modeset = True) | ||
59 | |||
60 | print("Crtc enable request returned %d\n" % r) | ||
61 | |||
62 | x = 0 | ||
63 | y = 0 | ||
64 | z = 0 | ||
65 | |||
66 | for plane in plane_list: | ||
67 | input("Press enter to enable plane idx %d on crtc idx %d" % | ||
68 | (plane.idx, crtc1.idx)) | ||
69 | req = pykms.AtomicReq(card) | ||
70 | req.add(plane, {"FB_ID": fb.id, | ||
71 | "CRTC_ID": crtc1.id, | ||
72 | "SRC_X": 0 << 16, | ||
73 | "SRC_Y": 0 << 16, | ||
74 | "SRC_W": fb.width << 16, | ||
75 | "SRC_H": fb.height << 16, | ||
76 | "CRTC_X": x, | ||
77 | "CRTC_Y": y, | ||
78 | "CRTC_W": fb.width, | ||
79 | "CRTC_H": fb.height, | ||
80 | "zorder": z}) | ||
81 | r = req.commit_sync() | ||
82 | print("Plane enable request returned %d\n" % r) | ||
83 | |||
84 | x = x + 50 | ||
85 | y = y + 50 | ||
86 | z = z + 1 | ||
87 | |||
88 | if not conn2: | ||
89 | sys.exit() | ||
90 | |||
91 | req = pykms.AtomicReq(card) | ||
92 | req.add(conn2, "CRTC_ID", crtc2.id) | ||
93 | req.add(crtc2, {"ACTIVE": 1, | ||
94 | "MODE_ID": modeb2.id}) | ||
95 | |||
96 | input("Press enter to enable crtc idx %d at %s" % (crtc2.idx, conn2.fullname)) | ||
97 | r = req.commit_sync(allow_modeset = True) | ||
98 | print("Crtc enable request returned %d\n" % r) | ||
99 | |||
100 | x = 0 | ||
101 | y = 0 | ||
102 | z = 0 | ||
103 | |||
104 | # Code assumes that planes for crtc1 also work for crtc2 | ||
105 | for plane in reversed(plane_list): | ||
106 | |||
107 | input("Press enter to disable plane idx %d on crtc idx %d" % | ||
108 | (plane.idx, crtc1.idx)) | ||
109 | req = pykms.AtomicReq(card) | ||
110 | req.add(plane, {"FB_ID": 0, | ||
111 | "CRTC_ID": 0}) | ||
112 | r = req.commit_sync(allow_modeset = True) | ||
113 | print("Plane disable request returned %d\n" % r) | ||
114 | |||
115 | input("Press enter to enable plane idx %d on crtc idx %d" % | ||
116 | (plane.idx, crtc2.idx)) | ||
117 | req = pykms.AtomicReq(card) | ||
118 | req.add(plane, {"FB_ID": fb.id, | ||
119 | "CRTC_ID": crtc2.id, | ||
120 | "SRC_X": 0 << 16, | ||
121 | "SRC_Y": 0 << 16, | ||
122 | "SRC_W": fb.width << 16, | ||
123 | "SRC_H": fb.height << 16, | ||
124 | "CRTC_X": x, | ||
125 | "CRTC_Y": y, | ||
126 | "CRTC_W": fb.width, | ||
127 | "CRTC_H": fb.height, | ||
128 | "zorder": z}) | ||
129 | r = req.commit_sync(allow_modeset = True) | ||
130 | print("Plane enable request returned %d\n" % r) | ||
131 | |||
132 | x = x + 50 | ||
133 | y = y + 50 | ||
134 | z = z + 1 | ||
135 | |||
136 | input("press enter to exit\n") | ||
diff --git a/py/tests/rottest.py b/py/tests/rottest.py new file mode 100755 index 0000000..8988134 --- /dev/null +++ b/py/tests/rottest.py | |||
@@ -0,0 +1,171 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | from enum import Enum | ||
5 | |||
6 | import termios, sys, os, tty | ||
7 | |||
8 | card = pykms.OmapCard() | ||
9 | |||
10 | res = pykms.ResourceManager(card) | ||
11 | conn = res.reserve_connector() | ||
12 | crtc = res.reserve_crtc(conn) | ||
13 | mode = conn.get_default_mode() | ||
14 | modeb = mode.to_blob(card) | ||
15 | rootplane = res.reserve_primary_plane(crtc, pykms.PixelFormat.XRGB8888) | ||
16 | plane = res.reserve_overlay_plane(crtc, pykms.PixelFormat.NV12) | ||
17 | |||
18 | card.disable_planes() | ||
19 | |||
20 | req = pykms.AtomicReq(card) | ||
21 | |||
22 | req.add(conn, "CRTC_ID", crtc.id) | ||
23 | |||
24 | req.add(crtc, {"ACTIVE": 1, | ||
25 | "MODE_ID": modeb.id}) | ||
26 | |||
27 | # This enables the root plane | ||
28 | |||
29 | #rootfb = pykms.OmapFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
30 | #pykms.draw_test_pattern(rootfb); | ||
31 | # | ||
32 | #req.add(rootplane, {"FB_ID": rootfb.id, | ||
33 | # "CRTC_ID": crtc.id, | ||
34 | # "SRC_X": 0 << 16, | ||
35 | # "SRC_Y": 0 << 16, | ||
36 | # "SRC_W": mode.hdisplay << 16, | ||
37 | # "SRC_H": mode.vdisplay << 16, | ||
38 | # "CRTC_X": 0, | ||
39 | # "CRTC_Y": 0, | ||
40 | # "CRTC_W": mode.hdisplay, | ||
41 | # "CRTC_H": mode.vdisplay, | ||
42 | # "zorder": 0}) | ||
43 | |||
44 | req.commit_sync(allow_modeset = True) | ||
45 | |||
46 | def show_rot_plane(crtc, plane, fb, rot, x_scale, y_scale): | ||
47 | |||
48 | crtc_w = int(fb_w * x_scale) | ||
49 | crtc_h = int(fb_h * y_scale) | ||
50 | |||
51 | if (rot & pykms.Rotation.ROTATE_90) or (rot & pykms.Rotation.ROTATE_270): | ||
52 | tmp = crtc_w | ||
53 | crtc_w = crtc_h | ||
54 | crtc_h = tmp | ||
55 | |||
56 | crtc_x = int(mode.hdisplay / 2 - crtc_w / 2) | ||
57 | crtc_y = int(mode.vdisplay / 2 - crtc_h / 2) | ||
58 | |||
59 | req = pykms.AtomicReq(card) | ||
60 | |||
61 | src_x = 0 | ||
62 | src_y = 0 | ||
63 | src_w = fb_w - src_x | ||
64 | src_h = fb_h - src_y | ||
65 | |||
66 | print("SRC {},{}-{}x{} DST {},{}-{}x{}".format( | ||
67 | src_x, src_y, src_w, src_h, | ||
68 | crtc_x, crtc_y, crtc_w, crtc_h)) | ||
69 | |||
70 | angle_str = pykms.Rotation(rot & pykms.Rotation.ROTATE_MASK).name | ||
71 | reflect_x_str = "REFLECT_X" if rot & pykms.Rotation.REFLECT_X else "" | ||
72 | reflect_y_str = "REFLECT_Y" if rot & pykms.Rotation.REFLECT_Y else "" | ||
73 | |||
74 | print("{} {} {}".format(angle_str, reflect_x_str, reflect_y_str)) | ||
75 | |||
76 | sys.stdout.flush() | ||
77 | |||
78 | req.add(plane, {"FB_ID": fb.id, | ||
79 | "CRTC_ID": crtc.id, | ||
80 | "SRC_X": src_x << 16, | ||
81 | "SRC_Y": src_y << 16, | ||
82 | "SRC_W": src_w << 16, | ||
83 | "SRC_H": src_h << 16, | ||
84 | "CRTC_X": crtc_x, | ||
85 | "CRTC_Y": crtc_y, | ||
86 | "CRTC_W": crtc_w, | ||
87 | "CRTC_H": crtc_h, | ||
88 | "rotation": rot, | ||
89 | "zorder": 2}) | ||
90 | |||
91 | req.commit_sync(allow_modeset = True) | ||
92 | |||
93 | |||
94 | fb_w = 480 | ||
95 | fb_h = 150 | ||
96 | x_scale = 1 | ||
97 | y_scale = 1 | ||
98 | |||
99 | fb = pykms.OmapFramebuffer(card, fb_w, fb_h, "NV12", flags = pykms.OmapFramebuffer.Tiled); | ||
100 | #fb = pykms.DumbFramebuffer(card, fb_w, fb_h, "NV12") | ||
101 | pykms.draw_test_pattern(fb); | ||
102 | |||
103 | def even(i): | ||
104 | return i & ~1 | ||
105 | |||
106 | pykms.draw_text(fb, even((fb_w // 2) - (8 * 3) // 2), 4, "TOP", pykms.white) | ||
107 | pykms.draw_text(fb, even((fb_w // 2) - (8 * 6) // 2), fb_h - 8 - 4, "BOTTOM", pykms.white) | ||
108 | pykms.draw_text(fb, 4, even(((fb_h // 2) - 4)), "L", pykms.white) | ||
109 | pykms.draw_text(fb, fb_w - 8 - 4, even(((fb_h // 2) - 4)), "R", pykms.white) | ||
110 | |||
111 | rots = [ pykms.Rotation.ROTATE_0, pykms.Rotation.ROTATE_90, pykms.Rotation.ROTATE_180, pykms.Rotation.ROTATE_270 ] | ||
112 | cursors = [ "A", "D", "B", "C" ] | ||
113 | |||
114 | print("Use the cursor keys, x and y to change rotation. Press q to quit.") | ||
115 | |||
116 | fd = sys.stdin.fileno() | ||
117 | oldterm = termios.tcgetattr(fd) | ||
118 | tty.setcbreak(fd) | ||
119 | |||
120 | try: | ||
121 | esc_seq = 0 | ||
122 | |||
123 | current_rot = pykms.Rotation.ROTATE_0 | ||
124 | |||
125 | show_rot_plane(crtc, plane, fb, current_rot, x_scale, y_scale) | ||
126 | |||
127 | while True: | ||
128 | c = sys.stdin.read(1) | ||
129 | #print("Got character {}".format(repr(c))) | ||
130 | |||
131 | changed = False | ||
132 | handled = False | ||
133 | |||
134 | if esc_seq == 0: | ||
135 | if c == "\x1b": | ||
136 | esc_seq = 1 | ||
137 | handled = True | ||
138 | elif esc_seq == 1: | ||
139 | if c == "[": | ||
140 | esc_seq = 2 | ||
141 | handled = True | ||
142 | else: | ||
143 | esc_seq = 0 | ||
144 | elif esc_seq == 2: | ||
145 | esc_seq = 0 | ||
146 | |||
147 | if c in cursors: | ||
148 | handled = True | ||
149 | |||
150 | rot = rots[cursors.index(c)] | ||
151 | |||
152 | current_rot &= ~pykms.Rotation.ROTATE_MASK | ||
153 | current_rot |= rot | ||
154 | |||
155 | changed = True | ||
156 | |||
157 | if not handled: | ||
158 | if c == "q": | ||
159 | break | ||
160 | elif c == "x": | ||
161 | current_rot ^= pykms.Rotation.REFLECT_X | ||
162 | changed = True | ||
163 | elif c == "y": | ||
164 | current_rot ^= pykms.Rotation.REFLECT_Y | ||
165 | changed = True | ||
166 | |||
167 | if changed: | ||
168 | show_rot_plane(crtc, plane, fb, current_rot, x_scale, y_scale) | ||
169 | |||
170 | finally: | ||
171 | termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm) | ||
diff --git a/py/tests/scale.py b/py/tests/scale.py new file mode 100755 index 0000000..0b97051 --- /dev/null +++ b/py/tests/scale.py | |||
@@ -0,0 +1,60 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import time | ||
5 | import random | ||
6 | |||
7 | card = pykms.Card() | ||
8 | res = pykms.ResourceManager(card) | ||
9 | conn = res.reserve_connector("hdmi") | ||
10 | crtc = res.reserve_crtc(conn) | ||
11 | plane = res.reserve_overlay_plane(crtc) | ||
12 | |||
13 | mode = conn.get_default_mode() | ||
14 | #mode = conn.get_mode(1920, 1080, 60, False) | ||
15 | |||
16 | # Blank framefuffer for primary plane | ||
17 | fb0 = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "AR24"); | ||
18 | |||
19 | crtc.set_mode(conn, fb0, mode) | ||
20 | |||
21 | # Initialize framebuffer for the scaled plane | ||
22 | fbX = 1920 | ||
23 | fbY = 1080 | ||
24 | fb = pykms.DumbFramebuffer(card, fbX, fbY, "RG16"); | ||
25 | pykms.draw_test_pattern(fb); | ||
26 | |||
27 | # Plane's scaled size and size increments | ||
28 | W = 72 | ||
29 | H = 54 | ||
30 | Winc = 1 | ||
31 | Hinc = 1 | ||
32 | |||
33 | # Plane's position and position increments | ||
34 | X = 0 | ||
35 | Y = 0 | ||
36 | Xinc = 1 | ||
37 | Yinc = 1 | ||
38 | while True: | ||
39 | print("+%d+%d %dx%d" % (X, Y, W, H)) | ||
40 | crtc.set_plane(plane, fb, X, Y, W, H, 0, 0, fbX, fbY) | ||
41 | W = W + Winc | ||
42 | H = H + Hinc | ||
43 | if (Winc == 1 and W >= mode.hdisplay - X): | ||
44 | Winc = -1 | ||
45 | if (Winc == -1 and W <= fbX/32): | ||
46 | Winc = 1 | ||
47 | if (Hinc == 1 and H >= mode.vdisplay - Y): | ||
48 | Hinc = -1 | ||
49 | if (Hinc == -1 and H <= fbY/32): | ||
50 | Hinc = 1 | ||
51 | X = X + Xinc | ||
52 | Y = Y + Yinc | ||
53 | if (Xinc == 1 and X >= mode.hdisplay - W): | ||
54 | Xinc = -1 | ||
55 | if (Xinc == -1 and X <= 0): | ||
56 | Xinc = 1 | ||
57 | if (Yinc == 1 and Y >= mode.vdisplay - H): | ||
58 | Yinc = -1 | ||
59 | if (Yinc == -1 and Y <= 0): | ||
60 | Yinc = 1 | ||
diff --git a/py/tests/sync.py b/py/tests/sync.py new file mode 100755 index 0000000..e394c8d --- /dev/null +++ b/py/tests/sync.py | |||
@@ -0,0 +1,234 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import ctypes | ||
4 | import fcntl | ||
5 | import os | ||
6 | import pykms | ||
7 | import selectors | ||
8 | import sys | ||
9 | import time | ||
10 | |||
11 | bar_width = 20 | ||
12 | bar_speed = 8 | ||
13 | |||
14 | class Timer(object): | ||
15 | timers = [] | ||
16 | |||
17 | def __init__(self, timeout, callback, data): | ||
18 | self.timeout = time.clock_gettime(time.CLOCK_MONOTONIC) + timeout | ||
19 | self.callback = callback | ||
20 | self.data = data | ||
21 | |||
22 | print("adding timer %f" % self.timeout) | ||
23 | self.timers.append(self) | ||
24 | self.timers.sort(key=lambda timer: timer.timeout) | ||
25 | |||
26 | @classmethod | ||
27 | def fire(_class): | ||
28 | clk = time.clock_gettime(time.CLOCK_MONOTONIC) | ||
29 | while len(_class.timers) > 0: | ||
30 | timer = _class.timers[0] | ||
31 | if timer.timeout > clk: | ||
32 | break | ||
33 | |||
34 | del _class.timers[0] | ||
35 | print("fireing timer %f" % timer.timeout) | ||
36 | timer.callback(timer.data) | ||
37 | |||
38 | @classmethod | ||
39 | def next_timeout(_class): | ||
40 | clk = time.clock_gettime(time.CLOCK_MONOTONIC) | ||
41 | if len(_class.timers) == 0 or _class.timers[0].timeout < clk: | ||
42 | return None | ||
43 | |||
44 | return _class.timers[0].timeout - clk | ||
45 | |||
46 | |||
47 | class Timeline(object): | ||
48 | |||
49 | class sw_sync_create_fence_data(ctypes.Structure): | ||
50 | _fields_ = [ | ||
51 | ('value', ctypes.c_uint32), | ||
52 | ('name', ctypes.c_char * 32), | ||
53 | ('fence', ctypes.c_int32), | ||
54 | ] | ||
55 | |||
56 | SW_SYNC_IOC_CREATE_FENCE = (3 << 30) | (ctypes.sizeof(sw_sync_create_fence_data) << 16) | (ord('W') << 8) | (0 << 0) | ||
57 | SW_SYNC_IOC_INC = (1 << 30) | (ctypes.sizeof(ctypes.c_uint32) << 16) | (ord('W') << 8) | (1 << 0) | ||
58 | |||
59 | class SWSync(object): | ||
60 | def __init__(self, fd): | ||
61 | self.fd = fd | ||
62 | def __del__(self): | ||
63 | os.close(self.fd) | ||
64 | |||
65 | def __init__(self): | ||
66 | self.value = 0 | ||
67 | try: | ||
68 | self.fd = os.open('/sys/kernel/debug/sync/sw_sync', 0); | ||
69 | except: | ||
70 | raise RuntimeError('Failed to open sw_sync file') | ||
71 | |||
72 | def close(self): | ||
73 | os.close(self.fd) | ||
74 | |||
75 | def create_fence(self, value): | ||
76 | data = self.sw_sync_create_fence_data(value = value); | ||
77 | print("ioctl number %u" % self.SW_SYNC_IOC_CREATE_FENCE) | ||
78 | ret = fcntl.ioctl(self.fd, self.SW_SYNC_IOC_CREATE_FENCE, data); | ||
79 | if ret < 0: | ||
80 | raise RuntimeError('Failed to create fence') | ||
81 | |||
82 | return self.SWSync(data.fence) | ||
83 | |||
84 | def signal(self, value): | ||
85 | fcntl.ioctl(self.fd, self.SW_SYNC_IOC_INC, ctypes.c_uint32(value)) | ||
86 | self.value += value | ||
87 | |||
88 | |||
89 | class FlipHandler(): | ||
90 | def __init__(self, crtc, width, height): | ||
91 | super().__init__() | ||
92 | self.crtc = crtc | ||
93 | self.timeline = Timeline() | ||
94 | self.bar_xpos = 0 | ||
95 | self.front_buf = 0 | ||
96 | self.fb1 = pykms.DumbFramebuffer(crtc.card, width, height, "XR24"); | ||
97 | self.fb2 = pykms.DumbFramebuffer(crtc.card, width, height, "XR24"); | ||
98 | self.flips = 0 | ||
99 | self.flips_last = 0 | ||
100 | self.frame_last = 0 | ||
101 | self.time_last = 0 | ||
102 | |||
103 | def handle_page_flip(self, frame, time): | ||
104 | if self.time_last == 0: | ||
105 | self.frame_last = frame | ||
106 | self.time_last = time | ||
107 | |||
108 | # Verify that the page flip hasn't completed before the timeline got | ||
109 | # signaled. | ||
110 | if self.timeline.value < 2 * self.flips - 1: | ||
111 | raise RuntimeError('Page flip %u for fence %u complete before timeline (%u)!' % | ||
112 | (self.flips, 2 * self.flips - 1, self.timeline.value)) | ||
113 | |||
114 | self.flips += 1 | ||
115 | |||
116 | # Print statistics every 5 seconds. | ||
117 | time_delta = time - self.time_last | ||
118 | if time_delta >= 5: | ||
119 | frame_delta = frame - self.frame_last | ||
120 | flips_delta = self.flips - self.flips_last | ||
121 | print("Frame rate: %f (%u/%u frames in %f s)" % | ||
122 | (frame_delta / time_delta, flips_delta, frame_delta, time_delta)) | ||
123 | |||
124 | self.frame_last = frame | ||
125 | self.flips_last = self.flips | ||
126 | self.time_last = time | ||
127 | |||
128 | # Draw the color bar on the back buffer. | ||
129 | if self.front_buf == 0: | ||
130 | fb = self.fb2 | ||
131 | else: | ||
132 | fb = self.fb1 | ||
133 | |||
134 | self.front_buf = self.front_buf ^ 1 | ||
135 | |||
136 | current_xpos = self.bar_xpos; | ||
137 | old_xpos = (current_xpos + (fb.width - bar_width - bar_speed)) % (fb.width - bar_width); | ||
138 | new_xpos = (current_xpos + bar_speed) % (fb.width - bar_width); | ||
139 | |||
140 | self.bar_xpos = new_xpos | ||
141 | |||
142 | pykms.draw_color_bar(fb, old_xpos, new_xpos, bar_width) | ||
143 | |||
144 | # Flip the buffers with an in fence located in the future. The atomic | ||
145 | # commit is asynchronous and returns immediately, but the flip should | ||
146 | # not complete before the fence gets signaled. | ||
147 | print("flipping with fence @%u, timeline is @%u" % (2 * self.flips - 1, self.timeline.value)) | ||
148 | fence = self.timeline.create_fence(2 * self.flips - 1) | ||
149 | req = pykms.AtomicReq(self.crtc.card) | ||
150 | req.add(self.crtc.primary_plane, { 'FB_ID': fb.id, 'IN_FENCE_FD': fence.fd }) | ||
151 | req.commit() | ||
152 | del fence | ||
153 | |||
154 | # Arm a timer to signal the fence in 0.5s. | ||
155 | def timeline_signal(timeline): | ||
156 | print("signaling timeline @%u" % timeline.value) | ||
157 | timeline.signal(2) | ||
158 | |||
159 | Timer(0.5, timeline_signal, self.timeline) | ||
160 | |||
161 | |||
162 | def main(argv): | ||
163 | if len(argv) > 1: | ||
164 | conn_name = argv[1] | ||
165 | else: | ||
166 | conn_name = '' | ||
167 | |||
168 | card = pykms.Card() | ||
169 | if not card.has_atomic: | ||
170 | raise RuntimeError('This test requires atomic update support') | ||
171 | |||
172 | res = pykms.ResourceManager(card) | ||
173 | conn = res.reserve_connector(conn_name) | ||
174 | crtc = res.reserve_crtc(conn) | ||
175 | mode = conn.get_default_mode() | ||
176 | |||
177 | flip_handler = FlipHandler(crtc, mode.hdisplay, mode.vdisplay) | ||
178 | |||
179 | fb = flip_handler.fb1 | ||
180 | pykms.draw_color_bar(fb, fb.width - bar_width - bar_speed, bar_speed, bar_width) | ||
181 | mode_blob = mode.blob(card) | ||
182 | |||
183 | req = pykms.AtomicReq(card) | ||
184 | req.add(conn, 'CRTC_ID', crtc.id) | ||
185 | req.add(crtc, { 'ACTIVE': 1, 'MODE_ID': mode_blob.id }) | ||
186 | req.add(crtc.primary_plane, { | ||
187 | 'FB_ID': fb.id, | ||
188 | 'CRTC_ID': crtc.id, | ||
189 | 'SRC_X': 0 << 16, | ||
190 | 'SRC_Y': 0 << 16, | ||
191 | 'SRC_W': fb.width << 16, | ||
192 | 'SRC_H': fb.height << 16, | ||
193 | 'CRTC_X': 0, | ||
194 | 'CRTC_Y': 0, | ||
195 | 'CRTC_W': fb.width, | ||
196 | 'CRTC_H': fb.height, | ||
197 | }) | ||
198 | ret = req.commit(flip_handler, allow_modeset = True) | ||
199 | if ret < 0: | ||
200 | raise RuntimeError('Atomic mode set failed with %d' % ret) | ||
201 | |||
202 | def bye(): | ||
203 | # Signal the timeline to complete all pending page flips | ||
204 | flip_handler.timeline.signal(100) | ||
205 | exit(0) | ||
206 | |||
207 | def readdrm(fileobj, mask): | ||
208 | for ev in card.read_events(): | ||
209 | if ev.type == pykms.DrmEventType.FLIP_COMPLETE: | ||
210 | flip_handler.handle_page_flip(ev.seq, ev.time) | ||
211 | |||
212 | def readkey(fileobj, mask): | ||
213 | sys.stdin.readline() | ||
214 | bye() | ||
215 | |||
216 | sel = selectors.DefaultSelector() | ||
217 | sel.register(card.fd, selectors.EVENT_READ, readdrm) | ||
218 | sel.register(sys.stdin, selectors.EVENT_READ, readkey) | ||
219 | |||
220 | while True: | ||
221 | timeout = Timer.next_timeout() | ||
222 | print("--> timeout %s" % repr(timeout)) | ||
223 | try: | ||
224 | events = sel.select(timeout) | ||
225 | except KeyboardInterrupt: | ||
226 | bye() | ||
227 | for key, mask in events: | ||
228 | callback = key.data | ||
229 | callback(key.fileobj, mask) | ||
230 | |||
231 | Timer.fire() | ||
232 | |||
233 | if __name__ == '__main__': | ||
234 | main(sys.argv) | ||
diff --git a/py/tests/test.py b/py/tests/test.py new file mode 100755 index 0000000..b7bf6bd --- /dev/null +++ b/py/tests/test.py | |||
@@ -0,0 +1,65 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import pykms | ||
5 | |||
6 | # draw test pattern via dmabuf? | ||
7 | dmabuf = False | ||
8 | |||
9 | # Use omap? | ||
10 | omap = False | ||
11 | |||
12 | if omap: | ||
13 | card = pykms.OmapCard() | ||
14 | else: | ||
15 | card = pykms.Card() | ||
16 | |||
17 | if len(sys.argv) > 1: | ||
18 | conn_name = sys.argv[1] | ||
19 | else: | ||
20 | conn_name = "" | ||
21 | |||
22 | res = pykms.ResourceManager(card) | ||
23 | conn = res.reserve_connector(conn_name) | ||
24 | crtc = res.reserve_crtc(conn) | ||
25 | plane = res.reserve_generic_plane(crtc) | ||
26 | mode = conn.get_default_mode() | ||
27 | modeb = mode.to_blob(card) | ||
28 | |||
29 | if omap: | ||
30 | origfb = pykms.OmapFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
31 | else: | ||
32 | origfb = pykms.DumbFramebuffer(card, mode.hdisplay, mode.vdisplay, "XR24"); | ||
33 | |||
34 | if dmabuf: | ||
35 | fb = pykms.ExtFramebuffer(card, origfb.width, origfb.height, origfb.format, | ||
36 | [origfb.fd(0)], [origfb.stride(0)], [origfb.offset(0)]) | ||
37 | else: | ||
38 | fb = origfb | ||
39 | |||
40 | pykms.draw_test_pattern(fb); | ||
41 | |||
42 | card.disable_planes() | ||
43 | |||
44 | req = pykms.AtomicReq(card) | ||
45 | |||
46 | req.add(conn, "CRTC_ID", crtc.id) | ||
47 | |||
48 | req.add(crtc, {"ACTIVE": 1, | ||
49 | "MODE_ID": modeb.id}) | ||
50 | |||
51 | req.add(plane, {"FB_ID": fb.id, | ||
52 | "CRTC_ID": crtc.id, | ||
53 | "SRC_X": 0 << 16, | ||
54 | "SRC_Y": 0 << 16, | ||
55 | "SRC_W": mode.hdisplay << 16, | ||
56 | "SRC_H": mode.vdisplay << 16, | ||
57 | "CRTC_X": 0, | ||
58 | "CRTC_Y": 0, | ||
59 | "CRTC_W": mode.hdisplay, | ||
60 | "CRTC_H": mode.vdisplay, | ||
61 | "zorder": 0}) | ||
62 | |||
63 | req.commit_sync(allow_modeset = True) | ||
64 | |||
65 | input("press enter to exit\n") | ||
diff --git a/py/tests/trans-test.py b/py/tests/trans-test.py new file mode 100755 index 0000000..8b8e6c5 --- /dev/null +++ b/py/tests/trans-test.py | |||
@@ -0,0 +1,331 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import pykms | ||
4 | import time | ||
5 | |||
6 | # This hack makes drm initialize the fbcon, setting up the default connector | ||
7 | card = pykms.Card() | ||
8 | card = 0 | ||
9 | |||
10 | card = pykms.Card() | ||
11 | res = pykms.ResourceManager(card) | ||
12 | conn = res.reserve_connector() | ||
13 | crtc = res.reserve_crtc(conn) | ||
14 | mode = conn.get_default_mode() | ||
15 | |||
16 | planes = [] | ||
17 | for p in card.planes: | ||
18 | if p.supports_crtc(crtc) == False: | ||
19 | continue | ||
20 | planes.append(p) | ||
21 | |||
22 | card.disable_planes() | ||
23 | |||
24 | w = mode.hdisplay | ||
25 | h = mode.vdisplay | ||
26 | |||
27 | fbs=[] | ||
28 | |||
29 | def test_am5_trans_dest(): | ||
30 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
31 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
32 | |||
33 | fb = fbs[0] | ||
34 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.purple) | ||
35 | pykms.draw_rect(fb, 100, 100, 100, 200, pykms.green) | ||
36 | pykms.draw_rect(fb, 300, 100, 100, 200, pykms.red) | ||
37 | pykms.draw_rect(fb, 500, 100, 100, 200, pykms.white) | ||
38 | |||
39 | fb = fbs[1] | ||
40 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.cyan) | ||
41 | pykms.draw_rect(fb, 250, 100, 200, 200, pykms.yellow) | ||
42 | |||
43 | crtc.set_props({ | ||
44 | "trans-key-mode": 1, | ||
45 | "trans-key": pykms.purple.rgb888, | ||
46 | "background": 0, | ||
47 | "alpha_blender": 0, | ||
48 | }) | ||
49 | |||
50 | plane = 0 | ||
51 | |||
52 | for i in range(0,2): | ||
53 | print("set crtc {}, plane {}, fb {}".format(crtc.id, planes[i].id, fbs[i].id)) | ||
54 | |||
55 | plane = planes[i] | ||
56 | fb = fbs[i] | ||
57 | plane.set_props({ | ||
58 | "FB_ID": fb.id, | ||
59 | "CRTC_ID": crtc.id, | ||
60 | "SRC_W": fb.width << 16, | ||
61 | "SRC_H": fb.height << 16, | ||
62 | "CRTC_W": fb.width, | ||
63 | "CRTC_H": fb.height, | ||
64 | "zorder": i, | ||
65 | }) | ||
66 | |||
67 | time.sleep(1) | ||
68 | |||
69 | def test_am5_trans_src(): | ||
70 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
71 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
72 | |||
73 | fb = fbs[0] | ||
74 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.white) | ||
75 | pykms.draw_rect(fb, 200, 200, 100, 100, pykms.red) | ||
76 | pykms.draw_rect(fb, fb.width - 300, 200, 100, 100, pykms.green) | ||
77 | |||
78 | fb = fbs[1] | ||
79 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.cyan) | ||
80 | pykms.draw_rect(fb, 100, 100, fb.width - 200, fb.height - 200, pykms.purple) | ||
81 | |||
82 | crtc.set_props({ | ||
83 | "trans-key-mode": 2, | ||
84 | "trans-key": pykms.purple.rgb888, | ||
85 | "background": 0, | ||
86 | "alpha_blender": 0, | ||
87 | }) | ||
88 | |||
89 | plane = 0 | ||
90 | |||
91 | for i in range(0,2): | ||
92 | print("set crtc {}, plane {}, fb {}".format(crtc.id, planes[i].id, fbs[i].id)) | ||
93 | |||
94 | plane = planes[i] | ||
95 | fb = fbs[i] | ||
96 | plane.set_props({ | ||
97 | "FB_ID": fb.id, | ||
98 | "CRTC_ID": crtc.id, | ||
99 | "SRC_W": fb.width << 16, | ||
100 | "SRC_H": fb.height << 16, | ||
101 | "CRTC_W": fb.width, | ||
102 | "CRTC_H": fb.height, | ||
103 | "zorder": 3 if i == 1 else 0, | ||
104 | }) | ||
105 | |||
106 | time.sleep(1) | ||
107 | |||
108 | def test_am4_normal_trans_dst(): | ||
109 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
110 | fbs.append(pykms.DumbFramebuffer(card, w * 2 // 3, h, "XR24")) | ||
111 | fbs.append(pykms.DumbFramebuffer(card, w * 2 // 3, h, "XR24")) | ||
112 | |||
113 | fb = fbs[0] | ||
114 | pykms.draw_rect(fb, 0, 0, w, h, pykms.purple) | ||
115 | pykms.draw_rect(fb, 100, 50, 50, 200, pykms.green) | ||
116 | pykms.draw_rect(fb, 200, 50, 50, 200, pykms.red) | ||
117 | pykms.draw_rect(fb, 300, 50, 50, 200, pykms.white) | ||
118 | |||
119 | fb = fbs[1] | ||
120 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.blue) | ||
121 | |||
122 | fb = fbs[2] | ||
123 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.cyan) | ||
124 | |||
125 | crtc.set_props({ | ||
126 | "trans-key-mode": 1, | ||
127 | "trans-key": pykms.purple.rgb888, | ||
128 | "background": 0, | ||
129 | "alpha_blender": 0, | ||
130 | }) | ||
131 | |||
132 | time.sleep(1) | ||
133 | |||
134 | plane = planes[0] | ||
135 | fb = fbs[0] | ||
136 | plane.set_props({ | ||
137 | "FB_ID": fb.id, | ||
138 | "CRTC_ID": crtc.id, | ||
139 | "SRC_W": fb.width << 16, | ||
140 | "SRC_H": fb.height << 16, | ||
141 | "CRTC_W": w, | ||
142 | "CRTC_H": h, | ||
143 | }) | ||
144 | |||
145 | time.sleep(1) | ||
146 | |||
147 | plane = planes[1] | ||
148 | fb = fbs[1] | ||
149 | plane.set_props({ | ||
150 | "FB_ID": fb.id, | ||
151 | "CRTC_ID": crtc.id, | ||
152 | "SRC_X": 0 << 16, | ||
153 | "SRC_Y": 0 << 16, | ||
154 | "SRC_W": fb.width << 16, | ||
155 | "SRC_H": fb.height << 16, | ||
156 | "CRTC_X": 0, | ||
157 | "CRTC_Y": 0, | ||
158 | "CRTC_W": fb.width, | ||
159 | "CRTC_H": fb.height, | ||
160 | }) | ||
161 | |||
162 | time.sleep(1) | ||
163 | |||
164 | plane = planes[2] | ||
165 | fb = fbs[2] | ||
166 | plane.set_props({ | ||
167 | "FB_ID": fb.id, | ||
168 | "CRTC_ID": crtc.id, | ||
169 | "SRC_X": 0 << 16, | ||
170 | "SRC_Y": 0 << 16, | ||
171 | "SRC_W": fb.width << 16, | ||
172 | "SRC_H": fb.height << 16, | ||
173 | "CRTC_X": w // 3, | ||
174 | "CRTC_Y": 0, | ||
175 | "CRTC_W": fb.width, | ||
176 | "CRTC_H": fb.height, | ||
177 | }) | ||
178 | |||
179 | def test_am4_normal_trans_src(): | ||
180 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
181 | fbs.append(pykms.DumbFramebuffer(card, w // 2, h, "XR24")) | ||
182 | fbs.append(pykms.DumbFramebuffer(card, w // 2, h, "XR24")) | ||
183 | |||
184 | fb = fbs[0] | ||
185 | pykms.draw_rect(fb, 0, 0, w, h, pykms.RGB(128, 255, 255)) | ||
186 | pykms.draw_rect(fb, 200, 100, 50, 200, pykms.red) | ||
187 | pykms.draw_rect(fb, w - 200 - 50, 100, 50, 200, pykms.green) | ||
188 | |||
189 | fb = fbs[1] | ||
190 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.blue) | ||
191 | pykms.draw_rect(fb, 100, 100, fb.width - 200, fb.height - 200, pykms.purple) | ||
192 | |||
193 | fb = fbs[2] | ||
194 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.cyan) | ||
195 | pykms.draw_rect(fb, 100, 100, fb.width - 200, fb.height - 200, pykms.purple) | ||
196 | |||
197 | crtc.set_props({ | ||
198 | "trans-key-mode": 2, | ||
199 | "trans-key": pykms.purple.rgb888, | ||
200 | "background": 0, | ||
201 | "alpha_blender": 0, | ||
202 | }) | ||
203 | |||
204 | time.sleep(1) | ||
205 | |||
206 | plane = planes[0] | ||
207 | fb = fbs[0] | ||
208 | plane.set_props({ | ||
209 | "FB_ID": fb.id, | ||
210 | "CRTC_ID": crtc.id, | ||
211 | "SRC_W": fb.width << 16, | ||
212 | "SRC_H": fb.height << 16, | ||
213 | "CRTC_W": w, | ||
214 | "CRTC_H": h, | ||
215 | }) | ||
216 | |||
217 | time.sleep(1) | ||
218 | |||
219 | plane = planes[1] | ||
220 | fb = fbs[1] | ||
221 | plane.set_props({ | ||
222 | "FB_ID": fb.id, | ||
223 | "CRTC_ID": crtc.id, | ||
224 | "SRC_X": 0 << 16, | ||
225 | "SRC_Y": 0 << 16, | ||
226 | "SRC_W": fb.width << 16, | ||
227 | "SRC_H": fb.height << 16, | ||
228 | "CRTC_X": 0, | ||
229 | "CRTC_Y": 0, | ||
230 | "CRTC_W": fb.width, | ||
231 | "CRTC_H": fb.height, | ||
232 | }) | ||
233 | |||
234 | time.sleep(1) | ||
235 | |||
236 | plane = planes[2] | ||
237 | fb = fbs[2] | ||
238 | plane.set_props({ | ||
239 | "FB_ID": fb.id, | ||
240 | "CRTC_ID": crtc.id, | ||
241 | "SRC_X": 0 << 16, | ||
242 | "SRC_Y": 0 << 16, | ||
243 | "SRC_W": fb.width << 16, | ||
244 | "SRC_H": fb.height << 16, | ||
245 | "CRTC_X": w - fb.width, | ||
246 | "CRTC_Y": 0, | ||
247 | "CRTC_W": fb.width, | ||
248 | "CRTC_H": fb.height, | ||
249 | }) | ||
250 | |||
251 | def test_am4_alpha_trans_src(): | ||
252 | fbs.append(pykms.DumbFramebuffer(card, w, h, "XR24")) | ||
253 | fbs.append(pykms.DumbFramebuffer(card, w // 2, h, "XR24")) | ||
254 | fbs.append(pykms.DumbFramebuffer(card, w // 2, h, "XR24")) | ||
255 | |||
256 | fb = fbs[0] | ||
257 | pykms.draw_rect(fb, 0, 0, w, h, pykms.purple) | ||
258 | pykms.draw_rect(fb, 200, 100, 50, 200, pykms.red) | ||
259 | pykms.draw_rect(fb, w - 200 - 50, 100, 50, 200, pykms.green) | ||
260 | |||
261 | fb = fbs[1] | ||
262 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.blue) | ||
263 | pykms.draw_rect(fb, 100, 100, fb.width - 200, fb.height - 200, pykms.purple) | ||
264 | |||
265 | fb = fbs[2] | ||
266 | pykms.draw_rect(fb, 0, 0, fb.width, fb.height, pykms.cyan) | ||
267 | pykms.draw_rect(fb, 100, 100, fb.width - 200, fb.height - 200, pykms.purple) | ||
268 | |||
269 | crtc.set_props({ | ||
270 | "trans-key-mode": 1, | ||
271 | "trans-key": pykms.purple.rgb888, | ||
272 | "background": 0, | ||
273 | "alpha_blender": 1, | ||
274 | }) | ||
275 | |||
276 | time.sleep(1) | ||
277 | |||
278 | plane = planes[0] | ||
279 | fb = fbs[0] | ||
280 | plane.set_props({ | ||
281 | "FB_ID": fb.id, | ||
282 | "CRTC_ID": crtc.id, | ||
283 | "SRC_W": fb.width << 16, | ||
284 | "SRC_H": fb.height << 16, | ||
285 | "CRTC_W": w, | ||
286 | "CRTC_H": h, | ||
287 | }) | ||
288 | |||
289 | time.sleep(1) | ||
290 | |||
291 | plane = planes[1] | ||
292 | fb = fbs[1] | ||
293 | plane.set_props({ | ||
294 | "FB_ID": fb.id, | ||
295 | "CRTC_ID": crtc.id, | ||
296 | "SRC_X": 0 << 16, | ||
297 | "SRC_Y": 0 << 16, | ||
298 | "SRC_W": fb.width << 16, | ||
299 | "SRC_H": fb.height << 16, | ||
300 | "CRTC_X": 0, | ||
301 | "CRTC_Y": 0, | ||
302 | "CRTC_W": fb.width, | ||
303 | "CRTC_H": fb.height, | ||
304 | }) | ||
305 | |||
306 | time.sleep(1) | ||
307 | |||
308 | plane = planes[2] | ||
309 | fb = fbs[2] | ||
310 | plane.set_props({ | ||
311 | "FB_ID": fb.id, | ||
312 | "CRTC_ID": crtc.id, | ||
313 | "SRC_X": 0 << 16, | ||
314 | "SRC_Y": 0 << 16, | ||
315 | "SRC_W": fb.width << 16, | ||
316 | "SRC_H": fb.height << 16, | ||
317 | "CRTC_X": w - fb.width, | ||
318 | "CRTC_Y": 0, | ||
319 | "CRTC_W": fb.width, | ||
320 | "CRTC_H": fb.height, | ||
321 | }) | ||
322 | |||
323 | |||
324 | |||
325 | #test_am5_trans_dest() | ||
326 | test_am5_trans_src() | ||
327 | #test_am4_normal_trans_dst() | ||
328 | #test_am4_normal_trans_src() | ||
329 | #test_am4_alpha_trans_src() | ||
330 | |||
331 | input("press enter to exit\n") | ||
diff --git a/scripts/dmt.py b/scripts/dmt.py new file mode 100755 index 0000000..82defb6 --- /dev/null +++ b/scripts/dmt.py | |||
@@ -0,0 +1,107 @@ | |||
1 | #!/usr/bin/python3 | ||
2 | |||
3 | import sys | ||
4 | import re | ||
5 | |||
6 | # Convert DMT pdf to txt: | ||
7 | # pdftotext -layout -f 18 -l 105 DMTr1\ v13.pdf DMT.txt | ||
8 | |||
9 | # Path to the text file | ||
10 | filepath = sys.argv[1] | ||
11 | |||
12 | m = {} | ||
13 | line = "" | ||
14 | |||
15 | def parsei(key, regex, base=10): | ||
16 | global m | ||
17 | global line | ||
18 | |||
19 | match = re.search(regex, line) | ||
20 | if match != None: | ||
21 | m[key] = int(match.group(1), base) | ||
22 | |||
23 | def parsef(key, regex, base=10): | ||
24 | global m | ||
25 | global line | ||
26 | |||
27 | match = re.search(regex, line) | ||
28 | if match != None: | ||
29 | m[key] = float(match.group(1)) | ||
30 | |||
31 | for line in open(filepath, 'r'): | ||
32 | # each page starts with this | ||
33 | if "VESA MONITOR TIMING STANDARD" in line: | ||
34 | m = { } | ||
35 | |||
36 | # each page ends with this | ||
37 | if "VESA Display Monitor Timing Standard" in line: | ||
38 | print("// {:#x} - {}".format(m["dmt_id"], m["name"])) | ||
39 | |||
40 | flags = [] | ||
41 | if m["ilace"]: | ||
42 | flags += [ "DRM_MODE_FLAG_INTERLACE" ] | ||
43 | |||
44 | if m["hsp"]: | ||
45 | flags += [ "DRM_MODE_FLAG_PHSYNC" ] | ||
46 | else: | ||
47 | flags += [ "DRM_MODE_FLAG_NHSYNC" ] | ||
48 | |||
49 | if m["vsp"]: | ||
50 | flags += [ "DRM_MODE_FLAG_PVSYNC" ] | ||
51 | else: | ||
52 | flags += [ "DRM_MODE_FLAG_NVSYNC" ] | ||
53 | |||
54 | print("DRM_MODE(\"{}\", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}),".format( | ||
55 | m["name"], | ||
56 | int(m["pclk"] * 1000), | ||
57 | m["hact"], m["hfp"], m["hsw"], m["hbp"], | ||
58 | m["vact"], m["vfp"], m["vsw"], m["vbp"], | ||
59 | " | ".join(flags) | ||
60 | )) | ||
61 | |||
62 | match = re.search("Timing Name\s+=\s+([^;]+)", line) | ||
63 | if match != None: | ||
64 | m["name"] = str.strip(match.group(1)) | ||
65 | |||
66 | parsei("dmt_id", "EDID ID:\s+DMT ID: ([0-9A-Fa-f]+)h", 16) | ||
67 | parsef("pclk", "Pixel Clock\s+=\s+(\d+\.\d+)") | ||
68 | |||
69 | parsei("hact", "Hor Pixels\s+=\s+(\d+)") | ||
70 | parsei("hfp", "H Front Porch.*\s(\d+) Pixels") | ||
71 | parsei("hsw", "Hor Sync Time.*\s(\d+) Pixels") | ||
72 | parsei("hbp", "H Back Porch.*\s(\d+) Pixels") | ||
73 | |||
74 | parsei("vact", "Ver Pixels\s+=\s+(\d+)") | ||
75 | parsei("vfp", "V Front Porch.*\s(\d+)\s+lines") | ||
76 | parsei("vsw", "Ver Sync Time.*\s(\d+)\s+lines") | ||
77 | parsei("vbp", "V Back Porch.*\s(\d+)\s+lines") | ||
78 | |||
79 | match = re.search("Scan Type\s+=\s+(\w+);", line) | ||
80 | if match != None: | ||
81 | if match.group(1) == "NONINTERLACED": | ||
82 | m["ilace"] = False | ||
83 | elif match.group(1) == "INTERLACED": | ||
84 | m["ilace"] = True | ||
85 | else: | ||
86 | print("Bad scan type") | ||
87 | exit(-1) | ||
88 | |||
89 | match = re.search("Hor Sync Polarity\s+=\s+(\w+)", line) | ||
90 | if match != None: | ||
91 | if match.group(1) == "POSITIVE": | ||
92 | m["hsp"] = True | ||
93 | elif match.group(1) == "NEGATIVE": | ||
94 | m["hsp"] = False | ||
95 | else: | ||
96 | print("Bad hsync polarity") | ||
97 | exit(-1) | ||
98 | |||
99 | match = re.search("Ver Sync Polarity\s+=\s+(\w+)", line) | ||
100 | if match != None: | ||
101 | if match.group(1) == "POSITIVE": | ||
102 | m["vsp"] = True | ||
103 | elif match.group(1) == "NEGATIVE": | ||
104 | m["vsp"] = False | ||
105 | else: | ||
106 | print("Bad vsync polarity") | ||
107 | exit(-1) | ||
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..f79b207 --- /dev/null +++ b/utils/CMakeLists.txt | |||
@@ -0,0 +1,29 @@ | |||
1 | include_directories(${LIBDRM_INCLUDE_DIRS}) | ||
2 | link_directories(${LIBDRM_LIBRARY_DIRS}) | ||
3 | |||
4 | add_executable (kmstest kmstest.cpp) | ||
5 | target_link_libraries(kmstest kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
6 | |||
7 | add_executable (kmsview kmsview.cpp) | ||
8 | target_link_libraries(kmsview kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
9 | |||
10 | add_executable (kmsprint kmsprint.cpp) | ||
11 | target_link_libraries(kmsprint kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
12 | |||
13 | add_executable (fbtest fbtest.cpp) | ||
14 | target_link_libraries(fbtest kms++util) | ||
15 | |||
16 | add_executable (kmscapture kmscapture.cpp) | ||
17 | target_link_libraries(kmscapture kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
18 | |||
19 | add_executable (kmsblank kmsblank.cpp) | ||
20 | target_link_libraries(kmsblank kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
21 | |||
22 | add_executable (wbcap wbcap.cpp) | ||
23 | target_link_libraries(wbcap kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
24 | |||
25 | add_executable (wbm2m wbm2m.cpp) | ||
26 | target_link_libraries(wbm2m kms++ kms++util ${LIBDRM_LIBRARIES}) | ||
27 | |||
28 | install(TARGETS kmstest kmsprint fbtest | ||
29 | DESTINATION bin) | ||
diff --git a/utils/fbtest.cpp b/utils/fbtest.cpp new file mode 100644 index 0000000..1c9a5f1 --- /dev/null +++ b/utils/fbtest.cpp | |||
@@ -0,0 +1,58 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include <errno.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | #include <sys/ioctl.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <sys/mman.h> | ||
11 | |||
12 | #include <linux/fb.h> | ||
13 | |||
14 | #include <kms++util/kms++util.h> | ||
15 | |||
16 | using namespace kms; | ||
17 | |||
18 | int main(int argc, char** argv) | ||
19 | { | ||
20 | const char* fbdev = "/dev/fb0"; | ||
21 | int r; | ||
22 | |||
23 | int fd = open(fbdev, O_RDWR); | ||
24 | FAIL_IF(fd < 0, "open %s failed\n", fbdev); | ||
25 | |||
26 | struct fb_var_screeninfo var; | ||
27 | |||
28 | r = ioctl(fd, FBIOGET_VSCREENINFO, &var); | ||
29 | FAIL_IF(r, "FBIOGET_VSCREENINFO failed"); | ||
30 | |||
31 | struct fb_fix_screeninfo fix; | ||
32 | |||
33 | r = ioctl(fd, FBIOGET_FSCREENINFO, &fix); | ||
34 | FAIL_IF(r, "FBIOGET_FSCREENINFO failed"); | ||
35 | |||
36 | uint8_t* ptr = (uint8_t*)mmap(NULL, | ||
37 | var.yres_virtual * fix.line_length, | ||
38 | PROT_WRITE | PROT_READ, | ||
39 | MAP_SHARED, fd, 0); | ||
40 | |||
41 | FAIL_IF(ptr == MAP_FAILED, "mmap failed"); | ||
42 | |||
43 | ExtCPUFramebuffer buf(var.xres, var.yres, PixelFormat::XRGB8888, | ||
44 | ptr, var.yres_virtual * fix.line_length, fix.line_length, 0); | ||
45 | |||
46 | printf("%s: res %dx%d, virtual %dx%d, line_len %d\n", | ||
47 | fbdev, | ||
48 | var.xres, var.yres, | ||
49 | var.xres_virtual, var.yres_virtual, | ||
50 | fix.line_length); | ||
51 | |||
52 | draw_test_pattern(buf); | ||
53 | draw_text(buf, buf.width() / 2, 0, fbdev, RGB(255, 255, 255)); | ||
54 | |||
55 | close(fd); | ||
56 | |||
57 | return 0; | ||
58 | } | ||
diff --git a/utils/kmsblank.cpp b/utils/kmsblank.cpp new file mode 100644 index 0000000..0b51810 --- /dev/null +++ b/utils/kmsblank.cpp | |||
@@ -0,0 +1,104 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <unistd.h> | ||
3 | #include <algorithm> | ||
4 | |||
5 | #include <kms++/kms++.h> | ||
6 | #include <kms++util/kms++util.h> | ||
7 | |||
8 | using namespace std; | ||
9 | using namespace kms; | ||
10 | |||
11 | static const char* usage_str = | ||
12 | "Usage: kmsblank [OPTION]...\n\n" | ||
13 | "Blank screen(s)\n\n" | ||
14 | "Options:\n" | ||
15 | " --device=DEVICE DEVICE is the path to DRM card to open\n" | ||
16 | " -c, --connector=CONN CONN is <connector>\n" | ||
17 | " -t, --time=TIME blank/unblank in TIME intervals\n" | ||
18 | "\n" | ||
19 | "<connector> can be given by index (<idx>) or id (@<id>).\n" | ||
20 | "<connector> can also be given by name.\n" | ||
21 | ; | ||
22 | |||
23 | static void usage() | ||
24 | { | ||
25 | puts(usage_str); | ||
26 | } | ||
27 | |||
28 | int main(int argc, char **argv) | ||
29 | { | ||
30 | string dev_path = "/dev/dri/card0"; | ||
31 | |||
32 | vector<string> conn_strs; | ||
33 | uint32_t time = 0; | ||
34 | |||
35 | OptionSet optionset = { | ||
36 | Option("|device=", [&dev_path](string s) | ||
37 | { | ||
38 | dev_path = s; | ||
39 | }), | ||
40 | Option("c|connector=", [&conn_strs](string str) | ||
41 | { | ||
42 | conn_strs.push_back(str); | ||
43 | }), | ||
44 | Option("t|time=", [&time](string str) | ||
45 | { | ||
46 | time = stoul(str); | ||
47 | }), | ||
48 | Option("h|help", []() | ||
49 | { | ||
50 | usage(); | ||
51 | exit(-1); | ||
52 | }), | ||
53 | }; | ||
54 | |||
55 | optionset.parse(argc, argv); | ||
56 | |||
57 | if (optionset.params().size() > 0) { | ||
58 | usage(); | ||
59 | exit(-1); | ||
60 | } | ||
61 | |||
62 | Card card(dev_path); | ||
63 | ResourceManager resman(card); | ||
64 | |||
65 | vector<Connector*> conns; | ||
66 | |||
67 | if (conn_strs.size() > 0) { | ||
68 | for (string s : conn_strs) { | ||
69 | auto c = resman.reserve_connector(s); | ||
70 | if (!c) | ||
71 | EXIT("Failed to resolve connector '%s'", s.c_str()); | ||
72 | conns.push_back(c); | ||
73 | } | ||
74 | } else { | ||
75 | conns = card.get_connectors(); | ||
76 | } | ||
77 | |||
78 | bool blank = true; | ||
79 | |||
80 | while (true) { | ||
81 | for (Connector* conn : conns) { | ||
82 | if (!conn->connected()) { | ||
83 | printf("Connector %u not connected\n", conn->idx()); | ||
84 | continue; | ||
85 | } | ||
86 | |||
87 | printf("Connector %u: %sblank\n", conn->idx(), blank ? "" : "un"); | ||
88 | int r = conn->set_prop_value("DPMS", blank ? 3 : 0); | ||
89 | if (r) | ||
90 | EXIT("Failed to set DPMS: %d", r); | ||
91 | } | ||
92 | |||
93 | if (time == 0) | ||
94 | break; | ||
95 | |||
96 | usleep(1000 * time); | ||
97 | |||
98 | blank = !blank; | ||
99 | } | ||
100 | |||
101 | printf("press enter to exit\n"); | ||
102 | |||
103 | getchar(); | ||
104 | } | ||
diff --git a/utils/kmscapture.cpp b/utils/kmscapture.cpp new file mode 100644 index 0000000..01eac61 --- /dev/null +++ b/utils/kmscapture.cpp | |||
@@ -0,0 +1,425 @@ | |||
1 | #include <linux/videodev2.h> | ||
2 | #include <cstdio> | ||
3 | #include <string.h> | ||
4 | #include <poll.h> | ||
5 | #include <fcntl.h> | ||
6 | #include <unistd.h> | ||
7 | #include <fstream> | ||
8 | #include <sys/ioctl.h> | ||
9 | #include <xf86drm.h> | ||
10 | #include <glob.h> | ||
11 | |||
12 | #include <kms++/kms++.h> | ||
13 | #include <kms++util/kms++util.h> | ||
14 | |||
15 | #define CAMERA_BUF_QUEUE_SIZE 3 | ||
16 | #define MAX_CAMERA 9 | ||
17 | |||
18 | using namespace std; | ||
19 | using namespace kms; | ||
20 | |||
21 | enum class BufferProvider { | ||
22 | DRM, | ||
23 | V4L2, | ||
24 | }; | ||
25 | |||
26 | class CameraPipeline | ||
27 | { | ||
28 | public: | ||
29 | CameraPipeline(int cam_fd, Card& card, Crtc* crtc, Plane* plane, uint32_t x, uint32_t y, | ||
30 | uint32_t iw, uint32_t ih, PixelFormat pixfmt, | ||
31 | BufferProvider buffer_provider); | ||
32 | ~CameraPipeline(); | ||
33 | |||
34 | CameraPipeline(const CameraPipeline& other) = delete; | ||
35 | CameraPipeline& operator=(const CameraPipeline& other) = delete; | ||
36 | |||
37 | void show_next_frame(AtomicReq &req); | ||
38 | int fd() const { return m_fd; } | ||
39 | void start_streaming(); | ||
40 | private: | ||
41 | ExtFramebuffer* GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt); | ||
42 | int m_fd; /* camera file descriptor */ | ||
43 | Crtc* m_crtc; | ||
44 | Plane* m_plane; | ||
45 | BufferProvider m_buffer_provider; | ||
46 | vector<Framebuffer*> m_fb; | ||
47 | int m_prev_fb_index; | ||
48 | uint32_t m_in_width, m_in_height; /* camera capture resolution */ | ||
49 | /* image properties for display */ | ||
50 | uint32_t m_out_width, m_out_height; | ||
51 | uint32_t m_out_x, m_out_y; | ||
52 | }; | ||
53 | |||
54 | static int buffer_export(int v4lfd, enum v4l2_buf_type bt, uint32_t index, int *dmafd) | ||
55 | { | ||
56 | struct v4l2_exportbuffer expbuf; | ||
57 | |||
58 | memset(&expbuf, 0, sizeof(expbuf)); | ||
59 | expbuf.type = bt; | ||
60 | expbuf.index = index; | ||
61 | if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) { | ||
62 | perror("VIDIOC_EXPBUF"); | ||
63 | return -1; | ||
64 | } | ||
65 | |||
66 | *dmafd = expbuf.fd; | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | ExtFramebuffer* CameraPipeline::GetExtFrameBuffer(Card& card, uint32_t i, PixelFormat pixfmt) | ||
72 | { | ||
73 | int r, dmafd; | ||
74 | |||
75 | r = buffer_export(m_fd, V4L2_BUF_TYPE_VIDEO_CAPTURE, i, &dmafd); | ||
76 | ASSERT(r == 0); | ||
77 | |||
78 | const PixelFormatInfo& format_info = get_pixel_format_info(pixfmt); | ||
79 | ASSERT(format_info.num_planes == 1); | ||
80 | |||
81 | vector<int> fds { dmafd }; | ||
82 | vector<uint32_t> pitches { m_in_width * (format_info.planes[0].bitspp / 8) }; | ||
83 | vector<uint32_t> offsets { 0 }; | ||
84 | |||
85 | return new ExtFramebuffer(card, m_in_width, m_in_height, pixfmt, | ||
86 | fds, pitches, offsets); | ||
87 | } | ||
88 | |||
89 | bool inline better_size(struct v4l2_frmsize_discrete* v4ldisc, | ||
90 | uint32_t iw, uint32_t ih, | ||
91 | uint32_t best_w, uint32_t best_h) | ||
92 | { | ||
93 | if (v4ldisc->width <= iw && v4ldisc->height <= ih && | ||
94 | (v4ldisc->width >= best_w || v4ldisc->height >= best_h)) | ||
95 | return true; | ||
96 | |||
97 | return false; | ||
98 | } | ||
99 | |||
100 | CameraPipeline::CameraPipeline(int cam_fd, Card& card, Crtc *crtc, Plane* plane, uint32_t x, uint32_t y, | ||
101 | uint32_t iw, uint32_t ih, PixelFormat pixfmt, | ||
102 | BufferProvider buffer_provider) | ||
103 | : m_fd(cam_fd), m_crtc(crtc), m_buffer_provider(buffer_provider), m_prev_fb_index(-1) | ||
104 | { | ||
105 | |||
106 | int r; | ||
107 | uint32_t best_w = 320; | ||
108 | uint32_t best_h = 240; | ||
109 | |||
110 | struct v4l2_frmsizeenum v4lfrms = { }; | ||
111 | v4lfrms.pixel_format = (uint32_t)pixfmt; | ||
112 | while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) { | ||
113 | if (v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE) { | ||
114 | v4lfrms.index++; | ||
115 | continue; | ||
116 | } | ||
117 | |||
118 | if (v4lfrms.discrete.width > iw || v4lfrms.discrete.height > ih) { | ||
119 | //skip | ||
120 | } else if (v4lfrms.discrete.width == iw && v4lfrms.discrete.height == ih) { | ||
121 | // Exact match | ||
122 | best_w = v4lfrms.discrete.width; | ||
123 | best_h = v4lfrms.discrete.height; | ||
124 | break; | ||
125 | } else if (v4lfrms.discrete.width >= best_w || v4lfrms.discrete.height >= ih) { | ||
126 | best_w = v4lfrms.discrete.width; | ||
127 | best_h = v4lfrms.discrete.height; | ||
128 | } | ||
129 | |||
130 | v4lfrms.index++; | ||
131 | }; | ||
132 | |||
133 | m_out_width = m_in_width = best_w; | ||
134 | m_out_height = m_in_height = best_h; | ||
135 | /* Move it to the middle of the requested area */ | ||
136 | m_out_x = x + iw / 2 - m_out_width / 2; | ||
137 | m_out_y = y + ih / 2 - m_out_height / 2; | ||
138 | |||
139 | printf("Capture: %ux%u\n", best_w, best_h); | ||
140 | |||
141 | struct v4l2_format v4lfmt = { }; | ||
142 | v4lfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
143 | r = ioctl(m_fd, VIDIOC_G_FMT, &v4lfmt); | ||
144 | ASSERT(r == 0); | ||
145 | |||
146 | v4lfmt.fmt.pix.pixelformat = (uint32_t)pixfmt; | ||
147 | v4lfmt.fmt.pix.width = m_in_width; | ||
148 | v4lfmt.fmt.pix.height = m_in_height; | ||
149 | |||
150 | r = ioctl(m_fd, VIDIOC_S_FMT, &v4lfmt); | ||
151 | ASSERT(r == 0); | ||
152 | |||
153 | uint32_t v4l_mem; | ||
154 | |||
155 | if (m_buffer_provider == BufferProvider::V4L2) | ||
156 | v4l_mem = V4L2_MEMORY_MMAP; | ||
157 | else | ||
158 | v4l_mem = V4L2_MEMORY_DMABUF; | ||
159 | |||
160 | struct v4l2_requestbuffers v4lreqbuf = { }; | ||
161 | v4lreqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
162 | v4lreqbuf.memory = v4l_mem; | ||
163 | v4lreqbuf.count = CAMERA_BUF_QUEUE_SIZE; | ||
164 | r = ioctl(m_fd, VIDIOC_REQBUFS, &v4lreqbuf); | ||
165 | ASSERT(r == 0); | ||
166 | ASSERT(v4lreqbuf.count == CAMERA_BUF_QUEUE_SIZE); | ||
167 | |||
168 | struct v4l2_buffer v4lbuf = { }; | ||
169 | v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
170 | v4lbuf.memory = v4l_mem; | ||
171 | |||
172 | for (unsigned i = 0; i < CAMERA_BUF_QUEUE_SIZE; i++) { | ||
173 | Framebuffer *fb; | ||
174 | |||
175 | if (m_buffer_provider == BufferProvider::V4L2) | ||
176 | fb = GetExtFrameBuffer(card, i, pixfmt); | ||
177 | else | ||
178 | fb = new DumbFramebuffer(card, m_in_width, | ||
179 | m_in_height, pixfmt); | ||
180 | |||
181 | v4lbuf.index = i; | ||
182 | if (m_buffer_provider == BufferProvider::DRM) | ||
183 | v4lbuf.m.fd = fb->prime_fd(0); | ||
184 | r = ioctl(m_fd, VIDIOC_QBUF, &v4lbuf); | ||
185 | ASSERT(r == 0); | ||
186 | |||
187 | m_fb.push_back(fb); | ||
188 | } | ||
189 | |||
190 | m_plane = plane; | ||
191 | |||
192 | // Do initial plane setup with first fb, so that we only need to | ||
193 | // set the FB when page flipping | ||
194 | AtomicReq req(card); | ||
195 | |||
196 | Framebuffer *fb = m_fb[0]; | ||
197 | |||
198 | req.add(m_plane, "CRTC_ID", m_crtc->id()); | ||
199 | req.add(m_plane, "FB_ID", fb->id()); | ||
200 | |||
201 | req.add(m_plane, "CRTC_X", m_out_x); | ||
202 | req.add(m_plane, "CRTC_Y", m_out_y); | ||
203 | req.add(m_plane, "CRTC_W", m_out_width); | ||
204 | req.add(m_plane, "CRTC_H", m_out_height); | ||
205 | |||
206 | req.add(m_plane, "SRC_X", 0); | ||
207 | req.add(m_plane, "SRC_Y", 0); | ||
208 | req.add(m_plane, "SRC_W", m_in_width << 16); | ||
209 | req.add(m_plane, "SRC_H", m_in_height << 16); | ||
210 | |||
211 | r = req.commit_sync(); | ||
212 | FAIL_IF(r, "initial plane setup failed"); | ||
213 | } | ||
214 | |||
215 | CameraPipeline::~CameraPipeline() | ||
216 | { | ||
217 | for (unsigned i = 0; i < m_fb.size(); i++) | ||
218 | delete m_fb[i]; | ||
219 | |||
220 | ::close(m_fd); | ||
221 | } | ||
222 | |||
223 | void CameraPipeline::start_streaming() | ||
224 | { | ||
225 | enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
226 | |||
227 | int r = ioctl(m_fd, VIDIOC_STREAMON, &type); | ||
228 | FAIL_IF(r, "Failed to enable camera stream: %d", r); | ||
229 | } | ||
230 | |||
231 | void CameraPipeline::show_next_frame(AtomicReq& req) | ||
232 | { | ||
233 | int r; | ||
234 | uint32_t v4l_mem; | ||
235 | |||
236 | if (m_buffer_provider == BufferProvider::V4L2) | ||
237 | v4l_mem = V4L2_MEMORY_MMAP; | ||
238 | else | ||
239 | v4l_mem = V4L2_MEMORY_DMABUF; | ||
240 | |||
241 | struct v4l2_buffer v4l2buf = { }; | ||
242 | v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
243 | v4l2buf.memory = v4l_mem; | ||
244 | r = ioctl(m_fd, VIDIOC_DQBUF, &v4l2buf); | ||
245 | if (r != 0) { | ||
246 | printf("VIDIOC_DQBUF ioctl failed with %d\n", errno); | ||
247 | return; | ||
248 | } | ||
249 | |||
250 | unsigned fb_index = v4l2buf.index; | ||
251 | |||
252 | Framebuffer *fb = m_fb[fb_index]; | ||
253 | |||
254 | req.add(m_plane, "FB_ID", fb->id()); | ||
255 | |||
256 | if (m_prev_fb_index >= 0) { | ||
257 | memset(&v4l2buf, 0, sizeof(v4l2buf)); | ||
258 | v4l2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
259 | v4l2buf.memory = v4l_mem; | ||
260 | v4l2buf.index = m_prev_fb_index; | ||
261 | if (m_buffer_provider == BufferProvider::DRM) | ||
262 | v4l2buf.m.fd = m_fb[m_prev_fb_index]->prime_fd(0); | ||
263 | r = ioctl(m_fd, VIDIOC_QBUF, &v4l2buf); | ||
264 | ASSERT(r == 0); | ||
265 | |||
266 | } | ||
267 | |||
268 | m_prev_fb_index = fb_index; | ||
269 | } | ||
270 | |||
271 | static bool is_capture_dev(int fd) | ||
272 | { | ||
273 | struct v4l2_capability cap = { }; | ||
274 | int r = ioctl(fd, VIDIOC_QUERYCAP, &cap); | ||
275 | ASSERT(r == 0); | ||
276 | return cap.capabilities & V4L2_CAP_VIDEO_CAPTURE; | ||
277 | } | ||
278 | |||
279 | std::vector<std::string> glob(const std::string& pat) | ||
280 | { | ||
281 | glob_t glob_result; | ||
282 | glob(pat.c_str(), 0, NULL, &glob_result); | ||
283 | vector<string> ret; | ||
284 | for(unsigned i = 0; i < glob_result.gl_pathc; ++i) | ||
285 | ret.push_back(string(glob_result.gl_pathv[i])); | ||
286 | globfree(&glob_result); | ||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | static const char* usage_str = | ||
291 | "Usage: kmscapture [OPTIONS]\n\n" | ||
292 | "Options:\n" | ||
293 | " -s, --single Single camera mode. Open only /dev/video0\n" | ||
294 | " --buffer-type=<drm|v4l> Use DRM or V4L provided buffers. Default: DRM\n" | ||
295 | " -h, --help Print this help\n" | ||
296 | ; | ||
297 | |||
298 | int main(int argc, char** argv) | ||
299 | { | ||
300 | BufferProvider buffer_provider = BufferProvider::DRM; | ||
301 | bool single_cam = false; | ||
302 | |||
303 | OptionSet optionset = { | ||
304 | Option("s|single", [&]() | ||
305 | { | ||
306 | single_cam = true; | ||
307 | }), | ||
308 | Option("|buffer-type=", [&](string s) | ||
309 | { | ||
310 | if (s == "v4l") | ||
311 | buffer_provider = BufferProvider::V4L2; | ||
312 | else if (s == "drm") | ||
313 | buffer_provider = BufferProvider::DRM; | ||
314 | else | ||
315 | FAIL("Invalid buffer provider: %s", s.c_str()); | ||
316 | }), | ||
317 | Option("h|help", [&]() | ||
318 | { | ||
319 | puts(usage_str); | ||
320 | exit(-1); | ||
321 | }), | ||
322 | }; | ||
323 | |||
324 | optionset.parse(argc, argv); | ||
325 | |||
326 | if (optionset.params().size() > 0) { | ||
327 | puts(usage_str); | ||
328 | exit(-1); | ||
329 | } | ||
330 | |||
331 | auto pixfmt = PixelFormat::YUYV; | ||
332 | |||
333 | Card card; | ||
334 | |||
335 | auto conn = card.get_first_connected_connector(); | ||
336 | auto crtc = conn->get_current_crtc(); | ||
337 | printf("Display: %dx%d\n", crtc->width(), crtc->height()); | ||
338 | printf("Buffer provider: %s\n", buffer_provider == BufferProvider::V4L2? "V4L" : "DRM"); | ||
339 | |||
340 | vector<int> camera_fds; | ||
341 | |||
342 | for (string vidpath : glob("/dev/video*")) { | ||
343 | int fd = ::open(vidpath.c_str(), O_RDWR | O_NONBLOCK); | ||
344 | |||
345 | if (fd < 0) | ||
346 | continue; | ||
347 | |||
348 | if (!is_capture_dev(fd)) { | ||
349 | close(fd); | ||
350 | continue; | ||
351 | } | ||
352 | |||
353 | camera_fds.push_back(fd); | ||
354 | printf("Using %s\n", vidpath.c_str()); | ||
355 | |||
356 | if (single_cam) | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | FAIL_IF(camera_fds.size() == 0, "No cameras found"); | ||
361 | |||
362 | vector<Plane*> available_planes; | ||
363 | for (Plane* p : crtc->get_possible_planes()) { | ||
364 | if (p->plane_type() != PlaneType::Overlay) | ||
365 | continue; | ||
366 | |||
367 | if (!p->supports_format(pixfmt)) | ||
368 | continue; | ||
369 | |||
370 | available_planes.push_back(p); | ||
371 | } | ||
372 | |||
373 | FAIL_IF(available_planes.size() < camera_fds.size(), "Not enough video planes for cameras"); | ||
374 | |||
375 | uint32_t plane_w = crtc->width() / camera_fds.size(); | ||
376 | vector<CameraPipeline*> cameras; | ||
377 | |||
378 | for (unsigned i = 0; i < camera_fds.size(); ++i) { | ||
379 | int cam_fd = camera_fds[i]; | ||
380 | Plane* plane = available_planes[i]; | ||
381 | |||
382 | auto cam = new CameraPipeline(cam_fd, card, crtc, plane, i * plane_w, 0, | ||
383 | plane_w, crtc->height(), pixfmt, buffer_provider); | ||
384 | cameras.push_back(cam); | ||
385 | } | ||
386 | |||
387 | unsigned nr_cameras = cameras.size(); | ||
388 | |||
389 | vector<pollfd> fds(nr_cameras + 1); | ||
390 | |||
391 | for (unsigned i = 0; i < nr_cameras; i++) { | ||
392 | fds[i].fd = cameras[i]->fd(); | ||
393 | fds[i].events = POLLIN; | ||
394 | } | ||
395 | fds[nr_cameras].fd = 0; | ||
396 | fds[nr_cameras].events = POLLIN; | ||
397 | |||
398 | for (auto cam : cameras) | ||
399 | cam->start_streaming(); | ||
400 | |||
401 | while (true) { | ||
402 | int r = poll(fds.data(), nr_cameras + 1, -1); | ||
403 | ASSERT(r > 0); | ||
404 | |||
405 | if (fds[nr_cameras].revents != 0) | ||
406 | break; | ||
407 | |||
408 | AtomicReq req(card); | ||
409 | |||
410 | for (unsigned i = 0; i < nr_cameras; i++) { | ||
411 | if (!fds[i].revents) | ||
412 | continue; | ||
413 | cameras[i]->show_next_frame(req); | ||
414 | fds[i].revents = 0; | ||
415 | } | ||
416 | |||
417 | r = req.test(); | ||
418 | FAIL_IF(r, "Atomic commit failed: %d", r); | ||
419 | |||
420 | req.commit_sync(); | ||
421 | } | ||
422 | |||
423 | for (auto cam : cameras) | ||
424 | delete cam; | ||
425 | } | ||
diff --git a/utils/kmsprint.cpp b/utils/kmsprint.cpp new file mode 100644 index 0000000..4d355fc --- /dev/null +++ b/utils/kmsprint.cpp | |||
@@ -0,0 +1,540 @@ | |||
1 | #include <algorithm> | ||
2 | #include <cinttypes> | ||
3 | #include <cstdio> | ||
4 | #include <iostream> | ||
5 | #include <string> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | #include <kms++/kms++.h> | ||
9 | #include <kms++util/kms++util.h> | ||
10 | |||
11 | using namespace std; | ||
12 | using namespace kms; | ||
13 | |||
14 | static struct { | ||
15 | bool print_props; | ||
16 | bool print_modes; | ||
17 | bool print_list; | ||
18 | bool x_modeline; | ||
19 | } s_opts; | ||
20 | |||
21 | static string format_mode(const Videomode& m, unsigned idx) | ||
22 | { | ||
23 | string str; | ||
24 | |||
25 | str = sformat(" %2u ", idx); | ||
26 | |||
27 | if (s_opts.x_modeline) { | ||
28 | str += sformat("%12s %6u %4u %4u %4u %4u %4u %4u %4u %4u %2u %#x %#x", | ||
29 | m.name.c_str(), | ||
30 | m.clock, | ||
31 | m.hdisplay, m.hsync_start, m.hsync_end, m.htotal, | ||
32 | m.vdisplay, m.vsync_start, m.vsync_end, m.vtotal, | ||
33 | m.vrefresh, | ||
34 | m.flags, | ||
35 | m.type); | ||
36 | } else { | ||
37 | string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); | ||
38 | string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); | ||
39 | |||
40 | str += sformat("%-12s %7.3f %-16s %-16s %2u (%.2f) %#10x %#6x", | ||
41 | m.name.c_str(), | ||
42 | m.clock / 1000.0, | ||
43 | h.c_str(), v.c_str(), | ||
44 | m.vrefresh, m.calculated_vrefresh(), | ||
45 | m.flags, | ||
46 | m.type); | ||
47 | } | ||
48 | |||
49 | return str; | ||
50 | } | ||
51 | |||
52 | static string format_mode_short(const Videomode& m) | ||
53 | { | ||
54 | string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); | ||
55 | string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); | ||
56 | |||
57 | return sformat("%s %.3f %s %s %u (%.2f)", | ||
58 | m.name.c_str(), | ||
59 | m.clock / 1000.0, | ||
60 | h.c_str(), v.c_str(), | ||
61 | m.vrefresh, m.calculated_vrefresh()); | ||
62 | } | ||
63 | |||
64 | static string format_connector(Connector& c) | ||
65 | { | ||
66 | string str; | ||
67 | |||
68 | str = sformat("Connector %u (%u) %s", | ||
69 | c.idx(), c.id(), c.fullname().c_str()); | ||
70 | |||
71 | if (c.connected()) | ||
72 | str += " (connected)"; | ||
73 | else | ||
74 | str += " (disconnected)"; | ||
75 | |||
76 | return str; | ||
77 | } | ||
78 | |||
79 | static string format_encoder(Encoder& e) | ||
80 | { | ||
81 | return sformat("Encoder %u (%u) %s", | ||
82 | e.idx(), e.id(), e.get_encoder_type().c_str()); | ||
83 | } | ||
84 | |||
85 | static string format_crtc(Crtc& c) | ||
86 | { | ||
87 | string str; | ||
88 | |||
89 | str = sformat("Crtc %u (%u)", c.idx(), c.id()); | ||
90 | |||
91 | if (c.mode_valid()) | ||
92 | str += " " + format_mode_short(c.mode()); | ||
93 | |||
94 | return str; | ||
95 | } | ||
96 | |||
97 | static string format_plane(Plane& p) | ||
98 | { | ||
99 | string str; | ||
100 | |||
101 | str = sformat("Plane %u (%u)", p.idx(), p.id()); | ||
102 | |||
103 | if (p.fb_id()) | ||
104 | str += sformat(" fb-id: %u", p.fb_id()); | ||
105 | |||
106 | string crtcs = join<Crtc*>(p.get_possible_crtcs(), " ", [](Crtc* crtc) { return to_string(crtc->idx()); }); | ||
107 | |||
108 | str += sformat(" (crtcs: %s)", crtcs.c_str()); | ||
109 | |||
110 | if (p.card().has_atomic()) { | ||
111 | str += sformat(" %u,%u %ux%u -> %u,%u %ux%u", | ||
112 | (uint32_t)p.get_prop_value("SRC_X") >> 16, | ||
113 | (uint32_t)p.get_prop_value("SRC_Y") >> 16, | ||
114 | (uint32_t)p.get_prop_value("SRC_W") >> 16, | ||
115 | (uint32_t)p.get_prop_value("SRC_H") >> 16, | ||
116 | (uint32_t)p.get_prop_value("CRTC_X"), | ||
117 | (uint32_t)p.get_prop_value("CRTC_Y"), | ||
118 | (uint32_t)p.get_prop_value("CRTC_W"), | ||
119 | (uint32_t)p.get_prop_value("CRTC_H")); | ||
120 | } | ||
121 | |||
122 | string fmts = join<PixelFormat>(p.get_formats(), " ", [](PixelFormat fmt) { return PixelFormatToFourCC(fmt); }); | ||
123 | |||
124 | str += sformat(" (%s)", fmts.c_str()); | ||
125 | |||
126 | return str; | ||
127 | } | ||
128 | |||
129 | static string format_fb(Framebuffer& fb) | ||
130 | { | ||
131 | return sformat("FB %u %ux%u", | ||
132 | fb.id(), fb.width(), fb.height()); | ||
133 | } | ||
134 | |||
135 | static string format_property(const Property* prop, uint64_t val) | ||
136 | { | ||
137 | string ret = sformat("%s = ", prop->name().c_str()); | ||
138 | |||
139 | switch (prop->type()) { | ||
140 | case PropertyType::Bitmask: | ||
141 | { | ||
142 | vector<string> v, vall; | ||
143 | |||
144 | for (auto kvp : prop->get_enums()) { | ||
145 | if (val & (1 << kvp.first)) | ||
146 | v.push_back(kvp.second); | ||
147 | vall.push_back(sformat("%s=0x%x", kvp.second.c_str(), 1 << kvp.first)); | ||
148 | } | ||
149 | |||
150 | ret += sformat("0x%" PRIx64 " (%s) [%s]", val, join(v, "|").c_str(), join(vall, "|").c_str()); | ||
151 | |||
152 | break; | ||
153 | } | ||
154 | |||
155 | case PropertyType::Blob: | ||
156 | { | ||
157 | uint32_t blob_id = (uint32_t)val; | ||
158 | |||
159 | if (blob_id) { | ||
160 | Blob blob(prop->card(), blob_id); | ||
161 | auto data = blob.data(); | ||
162 | |||
163 | ret += sformat("blob-id %u len %zu", blob_id, data.size()); | ||
164 | } else { | ||
165 | ret += sformat("blob-id %u", blob_id); | ||
166 | } | ||
167 | |||
168 | break; | ||
169 | } | ||
170 | |||
171 | case PropertyType::Enum: | ||
172 | { | ||
173 | string cur; | ||
174 | vector<string> vall; | ||
175 | |||
176 | for (auto kvp : prop->get_enums()) { | ||
177 | if (val == kvp.first) | ||
178 | cur = kvp.second; | ||
179 | vall.push_back(sformat("%s=%" PRIu64, kvp.second.c_str(), kvp.first)); | ||
180 | } | ||
181 | |||
182 | ret += sformat("%" PRIu64 " (%s) [%s]", val, cur.c_str(), join(vall, "|").c_str()); | ||
183 | |||
184 | break; | ||
185 | } | ||
186 | |||
187 | case PropertyType::Object: | ||
188 | { | ||
189 | ret += sformat("object id %u", (uint32_t)val); | ||
190 | break; | ||
191 | } | ||
192 | |||
193 | case PropertyType::Range: | ||
194 | { | ||
195 | auto values = prop->get_values(); | ||
196 | |||
197 | ret += sformat("%" PRIu64 " [%" PRIu64 " - %" PRIu64 "]", | ||
198 | val, values[0], values[1]); | ||
199 | |||
200 | break; | ||
201 | } | ||
202 | |||
203 | case PropertyType::SignedRange: | ||
204 | { | ||
205 | auto values = prop->get_values(); | ||
206 | |||
207 | ret += sformat("%" PRIi64 " [%" PRIi64 " - %" PRIi64 "]", | ||
208 | (int64_t)val, (int64_t)values[0], (int64_t)values[1]); | ||
209 | |||
210 | break; | ||
211 | } | ||
212 | |||
213 | } | ||
214 | |||
215 | if (prop->is_pending()) | ||
216 | ret += " (pending)"; | ||
217 | if (prop->is_immutable()) | ||
218 | ret += " (immutable)"; | ||
219 | |||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | static vector<string> format_props(DrmPropObject* o) | ||
224 | { | ||
225 | vector<string> lines; | ||
226 | |||
227 | auto pmap = o->get_prop_map(); | ||
228 | for (auto pp : pmap) { | ||
229 | const Property* p = o->card().get_prop(pp.first); | ||
230 | lines.push_back(format_property(p, pp.second)); | ||
231 | } | ||
232 | |||
233 | return lines; | ||
234 | } | ||
235 | |||
236 | static string format_ob(DrmObject* ob) | ||
237 | { | ||
238 | if (auto o = dynamic_cast<Connector*>(ob)) | ||
239 | return format_connector(*o); | ||
240 | else if (auto o = dynamic_cast<Encoder*>(ob)) | ||
241 | return format_encoder(*o); | ||
242 | else if (auto o = dynamic_cast<Crtc*>(ob)) | ||
243 | return format_crtc(*o); | ||
244 | else if (auto o = dynamic_cast<Plane*>(ob)) | ||
245 | return format_plane(*o); | ||
246 | else if (auto o = dynamic_cast<Framebuffer*>(ob)) | ||
247 | return format_fb(*o); | ||
248 | else | ||
249 | EXIT("Unkown DRM Object type\n"); | ||
250 | } | ||
251 | |||
252 | template<class T> | ||
253 | vector<T> filter(const vector<T>& sequence, function<bool(T)> predicate) | ||
254 | { | ||
255 | vector<T> result; | ||
256 | |||
257 | for(auto it = sequence.begin(); it != sequence.end(); ++it) | ||
258 | if(predicate(*it)) | ||
259 | result.push_back(*it); | ||
260 | |||
261 | return result; | ||
262 | } | ||
263 | |||
264 | struct Entry | ||
265 | { | ||
266 | string title; | ||
267 | vector<string> lines; | ||
268 | vector<Entry> children; | ||
269 | }; | ||
270 | |||
271 | static Entry& add_entry(vector<Entry>& entries) | ||
272 | { | ||
273 | entries.emplace_back(); | ||
274 | return entries.back(); | ||
275 | } | ||
276 | /* | ||
277 | static bool on_tty() | ||
278 | { | ||
279 | return isatty(STDOUT_FILENO) > 0; | ||
280 | } | ||
281 | */ | ||
282 | enum class TreeGlyphMode { | ||
283 | None, | ||
284 | ASCII, | ||
285 | UTF8, | ||
286 | }; | ||
287 | |||
288 | static TreeGlyphMode s_glyph_mode = TreeGlyphMode::None; | ||
289 | |||
290 | enum class TreeGlyph { | ||
291 | Vertical, | ||
292 | Branch, | ||
293 | Right, | ||
294 | Space, | ||
295 | }; | ||
296 | |||
297 | static const map<TreeGlyph, string> glyphs_utf8 = { | ||
298 | { TreeGlyph::Vertical, "│ " }, | ||
299 | { TreeGlyph::Branch, "├─" }, | ||
300 | { TreeGlyph::Right, "└─" }, | ||
301 | { TreeGlyph::Space, " " }, | ||
302 | |||
303 | }; | ||
304 | |||
305 | static const map<TreeGlyph, string> glyphs_ascii = { | ||
306 | { TreeGlyph::Vertical, "| " }, | ||
307 | { TreeGlyph::Branch, "|-" }, | ||
308 | { TreeGlyph::Right, "`-" }, | ||
309 | { TreeGlyph::Space, " " }, | ||
310 | |||
311 | }; | ||
312 | |||
313 | const char* get_glyph(TreeGlyph glyph) | ||
314 | { | ||
315 | if (s_glyph_mode == TreeGlyphMode::None) | ||
316 | return " "; | ||
317 | |||
318 | const map<TreeGlyph, string>& glyphs = s_glyph_mode == TreeGlyphMode::UTF8 ? glyphs_utf8 : glyphs_ascii; | ||
319 | |||
320 | return glyphs.at(glyph).c_str(); | ||
321 | } | ||
322 | |||
323 | static void print_entry(const Entry& e, const string& prefix, bool is_child, bool is_last) | ||
324 | { | ||
325 | string prefix1; | ||
326 | string prefix2; | ||
327 | |||
328 | if (is_child) { | ||
329 | prefix1 = prefix + (is_last ? get_glyph(TreeGlyph::Right) : get_glyph(TreeGlyph::Branch)); | ||
330 | prefix2 = prefix + (is_last ? get_glyph(TreeGlyph::Space) : get_glyph(TreeGlyph::Vertical)); | ||
331 | } | ||
332 | |||
333 | printf("%s%s\n", prefix1.c_str(), e.title.c_str()); | ||
334 | |||
335 | bool has_children = e.children.size() > 0; | ||
336 | |||
337 | string data_prefix = prefix2 + (has_children ? get_glyph(TreeGlyph::Vertical) : get_glyph(TreeGlyph::Space)); | ||
338 | |||
339 | for (const string& str : e.lines) { | ||
340 | string p = data_prefix + get_glyph(TreeGlyph::Space); | ||
341 | printf("%s%s\n", p.c_str(), str.c_str()); | ||
342 | } | ||
343 | |||
344 | for (const Entry& child : e.children) { | ||
345 | bool is_last = &child == &e.children.back(); | ||
346 | |||
347 | print_entry(child, prefix2, true, is_last); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | static void print_entries(const vector<Entry>& entries, const string& prefix) | ||
352 | { | ||
353 | for (const Entry& e: entries) { | ||
354 | print_entry(e, "", false, false); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | template<class T> | ||
359 | static void append(vector<DrmObject*>& dst, const vector<T*>& src) | ||
360 | { | ||
361 | dst.insert(dst.end(), src.begin(), src.end()); | ||
362 | } | ||
363 | |||
364 | |||
365 | static void print_as_list(Card& card) | ||
366 | { | ||
367 | vector<DrmPropObject*> obs; | ||
368 | vector<Framebuffer*> fbs; | ||
369 | |||
370 | for (Connector* conn : card.get_connectors()) { | ||
371 | obs.push_back(conn); | ||
372 | } | ||
373 | |||
374 | for (Encoder* enc : card.get_encoders()) { | ||
375 | obs.push_back(enc); | ||
376 | } | ||
377 | |||
378 | for (Crtc* crtc : card.get_crtcs()) { | ||
379 | obs.push_back(crtc); | ||
380 | if (crtc->buffer_id() && !card.has_has_universal_planes()) { | ||
381 | Framebuffer* fb = new Framebuffer(card, crtc->buffer_id()); | ||
382 | fbs.push_back(fb); | ||
383 | } | ||
384 | } | ||
385 | |||
386 | for (Plane* plane : card.get_planes()) { | ||
387 | obs.push_back(plane); | ||
388 | if (plane->fb_id()) { | ||
389 | Framebuffer* fb = new Framebuffer(card, plane->fb_id()); | ||
390 | fbs.push_back(fb); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | for (DrmPropObject* ob: obs) { | ||
395 | printf("%s\n", format_ob(ob).c_str()); | ||
396 | |||
397 | if (s_opts.print_props) { | ||
398 | for (string str : format_props(ob)) | ||
399 | printf(" %s\n", str.c_str()); | ||
400 | } | ||
401 | } | ||
402 | |||
403 | for (Framebuffer* fb: fbs) { | ||
404 | printf("%s\n", format_ob(fb).c_str()); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | static void print_as_tree(Card& card) | ||
409 | { | ||
410 | vector<Entry> entries; | ||
411 | |||
412 | for (Connector* conn : card.get_connectors()) { | ||
413 | Entry& e1 = add_entry(entries); | ||
414 | e1.title = format_ob(conn); | ||
415 | if (s_opts.print_props) | ||
416 | e1.lines = format_props(conn); | ||
417 | |||
418 | for (Encoder* enc : conn->get_encoders()) { | ||
419 | |||
420 | Entry& e2 = add_entry(e1.children); | ||
421 | e2.title = format_ob(enc); | ||
422 | if (s_opts.print_props) | ||
423 | e2.lines = format_props(enc); | ||
424 | |||
425 | if (Crtc* crtc = enc->get_crtc()) { | ||
426 | Entry& e3 = add_entry(e2.children); | ||
427 | e3.title = format_ob(crtc); | ||
428 | if (s_opts.print_props) | ||
429 | e3.lines = format_props(crtc); | ||
430 | |||
431 | if (crtc->buffer_id() && !card.has_has_universal_planes()) { | ||
432 | Framebuffer fb(card, crtc->buffer_id()); | ||
433 | Entry& e5 = add_entry(e3.children); | ||
434 | |||
435 | e5.title = format_ob(&fb); | ||
436 | } | ||
437 | |||
438 | for (Plane* plane : card.get_planes()) { | ||
439 | if (plane->crtc_id() != crtc->id()) | ||
440 | continue; | ||
441 | |||
442 | Entry& e4 = add_entry(e3.children); | ||
443 | e4.title = format_ob(plane); | ||
444 | if (s_opts.print_props) | ||
445 | e4.lines = format_props(plane); | ||
446 | |||
447 | uint32_t fb_id = plane->fb_id(); | ||
448 | if (fb_id) { | ||
449 | Framebuffer fb(card, fb_id); | ||
450 | |||
451 | Entry& e5 = add_entry(e4.children); | ||
452 | |||
453 | e5.title = format_ob(&fb); | ||
454 | } | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | |||
460 | print_entries(entries, ""); | ||
461 | } | ||
462 | |||
463 | static void print_modes(Card& card) | ||
464 | { | ||
465 | for (Connector* conn : card.get_connectors()) { | ||
466 | if (!conn->connected()) | ||
467 | continue; | ||
468 | |||
469 | printf("%s\n", format_ob(conn).c_str()); | ||
470 | |||
471 | auto modes = conn->get_modes(); | ||
472 | for (unsigned i = 0; i < modes.size(); ++i) | ||
473 | printf("%s\n", format_mode(modes[i], i).c_str()); | ||
474 | } | ||
475 | } | ||
476 | |||
477 | static const char* usage_str = | ||
478 | "Usage: kmsprint [OPTIONS]\n\n" | ||
479 | "Options:\n" | ||
480 | " -l, --list Print list instead of tree\n" | ||
481 | " -m, --modes Print modes\n" | ||
482 | " --xmode Print modes using X modeline\n" | ||
483 | " -p, --props Print properties\n" | ||
484 | ; | ||
485 | |||
486 | static void usage() | ||
487 | { | ||
488 | puts(usage_str); | ||
489 | } | ||
490 | |||
491 | int main(int argc, char **argv) | ||
492 | { | ||
493 | string dev_path = "/dev/dri/card0"; | ||
494 | |||
495 | OptionSet optionset = { | ||
496 | Option("|device=", [&dev_path](string s) | ||
497 | { | ||
498 | dev_path = s; | ||
499 | }), | ||
500 | Option("l|list", []() | ||
501 | { | ||
502 | s_opts.print_list = true; | ||
503 | }), | ||
504 | Option("m|modes", []() | ||
505 | { | ||
506 | s_opts.print_modes = true; | ||
507 | }), | ||
508 | Option("p|props", []() | ||
509 | { | ||
510 | s_opts.print_props = true; | ||
511 | }), | ||
512 | Option("|xmode", []() { | ||
513 | s_opts.x_modeline = true; | ||
514 | }), | ||
515 | Option("h|help", []() | ||
516 | { | ||
517 | usage(); | ||
518 | exit(-1); | ||
519 | }), | ||
520 | }; | ||
521 | |||
522 | optionset.parse(argc, argv); | ||
523 | |||
524 | if (optionset.params().size() > 0) { | ||
525 | usage(); | ||
526 | exit(-1); | ||
527 | } | ||
528 | |||
529 | Card card(dev_path); | ||
530 | |||
531 | if (s_opts.print_modes) { | ||
532 | print_modes(card); | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | if (s_opts.print_list) | ||
537 | print_as_list(card); | ||
538 | else | ||
539 | print_as_tree(card); | ||
540 | } | ||
diff --git a/utils/kmstest.cpp b/utils/kmstest.cpp new file mode 100644 index 0000000..62b103f --- /dev/null +++ b/utils/kmstest.cpp | |||
@@ -0,0 +1,1108 @@ | |||
1 | #include <cstdio> | ||
2 | #include <cstring> | ||
3 | #include <algorithm> | ||
4 | #include <regex> | ||
5 | #include <set> | ||
6 | #include <chrono> | ||
7 | #include <cstdint> | ||
8 | #include <cinttypes> | ||
9 | |||
10 | #include <sys/select.h> | ||
11 | |||
12 | #include <kms++/kms++.h> | ||
13 | #include <kms++/modedb.h> | ||
14 | #include <kms++/mode_cvt.h> | ||
15 | |||
16 | #include <kms++util/kms++util.h> | ||
17 | |||
18 | using namespace std; | ||
19 | using namespace kms; | ||
20 | |||
21 | struct PropInfo { | ||
22 | PropInfo(string n, uint64_t v) : prop(NULL), name(n), val(v) {} | ||
23 | |||
24 | Property *prop; | ||
25 | string name; | ||
26 | uint64_t val; | ||
27 | }; | ||
28 | |||
29 | struct PlaneInfo | ||
30 | { | ||
31 | Plane* plane; | ||
32 | |||
33 | unsigned x; | ||
34 | unsigned y; | ||
35 | unsigned w; | ||
36 | unsigned h; | ||
37 | |||
38 | unsigned view_x; | ||
39 | unsigned view_y; | ||
40 | unsigned view_w; | ||
41 | unsigned view_h; | ||
42 | |||
43 | vector<Framebuffer*> fbs; | ||
44 | |||
45 | vector<PropInfo> props; | ||
46 | }; | ||
47 | |||
48 | struct OutputInfo | ||
49 | { | ||
50 | Connector* connector; | ||
51 | |||
52 | Crtc* crtc; | ||
53 | Videomode mode; | ||
54 | vector<Framebuffer*> legacy_fbs; | ||
55 | |||
56 | vector<PlaneInfo> planes; | ||
57 | |||
58 | vector<PropInfo> conn_props; | ||
59 | vector<PropInfo> crtc_props; | ||
60 | }; | ||
61 | |||
62 | static bool s_use_dmt; | ||
63 | static bool s_use_cea; | ||
64 | static unsigned s_num_buffers = 1; | ||
65 | static bool s_flip_mode; | ||
66 | static bool s_flip_sync; | ||
67 | static bool s_cvt; | ||
68 | static bool s_cvt_v2; | ||
69 | static bool s_cvt_vid_opt; | ||
70 | static unsigned s_max_flips; | ||
71 | |||
72 | __attribute__ ((unused)) | ||
73 | static void print_regex_match(smatch sm) | ||
74 | { | ||
75 | for (unsigned i = 0; i < sm.size(); ++i) { | ||
76 | string str = sm[i].str(); | ||
77 | printf("%u: %s\n", i, str.c_str()); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static void get_connector(ResourceManager& resman, OutputInfo& output, const string& str = "") | ||
82 | { | ||
83 | Connector* conn = resman.reserve_connector(str); | ||
84 | |||
85 | if (!conn) | ||
86 | EXIT("No connector '%s'", str.c_str()); | ||
87 | |||
88 | if (!conn->connected()) | ||
89 | EXIT("Connector '%s' not connected", conn->fullname().c_str()); | ||
90 | |||
91 | output.connector = conn; | ||
92 | output.mode = output.connector->get_default_mode(); | ||
93 | } | ||
94 | |||
95 | static void get_default_crtc(ResourceManager& resman, OutputInfo& output) | ||
96 | { | ||
97 | output.crtc = resman.reserve_crtc(output.connector); | ||
98 | |||
99 | if (!output.crtc) | ||
100 | EXIT("Could not find available crtc"); | ||
101 | } | ||
102 | |||
103 | |||
104 | static PlaneInfo *add_default_planeinfo(OutputInfo* output) | ||
105 | { | ||
106 | output->planes.push_back(PlaneInfo { }); | ||
107 | PlaneInfo *ret = &output->planes.back(); | ||
108 | ret->w = output->mode.hdisplay; | ||
109 | ret->h = output->mode.vdisplay; | ||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | static void parse_crtc(ResourceManager& resman, Card& card, const string& crtc_str, OutputInfo& output) | ||
114 | { | ||
115 | // @12:1920x1200i@60 | ||
116 | // @12:33000000,800/210/30/16/-,480/22/13/10/-,i | ||
117 | |||
118 | const regex modename_re("(?:(@?)(\\d+):)?" // @12: | ||
119 | "(?:(\\d+)x(\\d+)(i)?)" // 1920x1200i | ||
120 | "(?:@([\\d\\.]+))?"); // @60 | ||
121 | |||
122 | const regex modeline_re("(?:(@?)(\\d+):)?" // @12: | ||
123 | "(\\d+)," // 33000000, | ||
124 | "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-])," // 800/210/30/16/-, | ||
125 | "(\\d+)/(\\d+)/(\\d+)/(\\d+)/([+-])" // 480/22/13/10/- | ||
126 | "(?:,([i]+))?" // ,i | ||
127 | ); | ||
128 | |||
129 | smatch sm; | ||
130 | if (regex_match(crtc_str, sm, modename_re)) { | ||
131 | if (sm[2].matched) { | ||
132 | bool use_id = sm[1].length() == 1; | ||
133 | unsigned num = stoul(sm[2].str()); | ||
134 | |||
135 | if (use_id) { | ||
136 | Crtc* c = card.get_crtc(num); | ||
137 | if (!c) | ||
138 | EXIT("Bad crtc id '%u'", num); | ||
139 | |||
140 | output.crtc = c; | ||
141 | } else { | ||
142 | auto crtcs = card.get_crtcs(); | ||
143 | |||
144 | if (num >= crtcs.size()) | ||
145 | EXIT("Bad crtc number '%u'", num); | ||
146 | |||
147 | output.crtc = crtcs[num]; | ||
148 | } | ||
149 | } else { | ||
150 | output.crtc = output.connector->get_current_crtc(); | ||
151 | } | ||
152 | |||
153 | unsigned w = stoul(sm[3]); | ||
154 | unsigned h = stoul(sm[4]); | ||
155 | bool ilace = sm[5].matched ? true : false; | ||
156 | float refresh = sm[6].matched ? stof(sm[6]) : 0; | ||
157 | |||
158 | if (s_cvt) { | ||
159 | output.mode = videomode_from_cvt(w, h, refresh, ilace, s_cvt_v2, s_cvt_vid_opt); | ||
160 | } else if (s_use_dmt) { | ||
161 | try { | ||
162 | output.mode = find_dmt(w, h, refresh, ilace); | ||
163 | } catch (exception& e) { | ||
164 | EXIT("Mode not found from DMT tables\n"); | ||
165 | } | ||
166 | } else if (s_use_cea) { | ||
167 | try { | ||
168 | output.mode = find_cea(w, h, refresh, ilace); | ||
169 | } catch (exception& e) { | ||
170 | EXIT("Mode not found from CEA tables\n"); | ||
171 | } | ||
172 | } else { | ||
173 | try { | ||
174 | output.mode = output.connector->get_mode(w, h, refresh, ilace); | ||
175 | } catch (exception& e) { | ||
176 | EXIT("Mode not found from the connector\n"); | ||
177 | } | ||
178 | } | ||
179 | } else if (regex_match(crtc_str, sm, modeline_re)) { | ||
180 | if (sm[2].matched) { | ||
181 | bool use_id = sm[1].length() == 1; | ||
182 | unsigned num = stoul(sm[2].str()); | ||
183 | |||
184 | if (use_id) { | ||
185 | Crtc* c = card.get_crtc(num); | ||
186 | if (!c) | ||
187 | EXIT("Bad crtc id '%u'", num); | ||
188 | |||
189 | output.crtc = c; | ||
190 | } else { | ||
191 | auto crtcs = card.get_crtcs(); | ||
192 | |||
193 | if (num >= crtcs.size()) | ||
194 | EXIT("Bad crtc number '%u'", num); | ||
195 | |||
196 | output.crtc = crtcs[num]; | ||
197 | } | ||
198 | } else { | ||
199 | output.crtc = output.connector->get_current_crtc(); | ||
200 | } | ||
201 | |||
202 | unsigned clock = stoul(sm[3]); | ||
203 | |||
204 | unsigned hact = stoul(sm[4]); | ||
205 | unsigned hfp = stoul(sm[5]); | ||
206 | unsigned hsw = stoul(sm[6]); | ||
207 | unsigned hbp = stoul(sm[7]); | ||
208 | bool h_pos_sync = sm[8] == "+" ? true : false; | ||
209 | |||
210 | unsigned vact = stoul(sm[9]); | ||
211 | unsigned vfp = stoul(sm[10]); | ||
212 | unsigned vsw = stoul(sm[11]); | ||
213 | unsigned vbp = stoul(sm[12]); | ||
214 | bool v_pos_sync = sm[13] == "+" ? true : false; | ||
215 | |||
216 | output.mode = videomode_from_timings(clock / 1000, hact, hfp, hsw, hbp, vact, vfp, vsw, vbp); | ||
217 | output.mode.set_hsync(h_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative); | ||
218 | output.mode.set_vsync(v_pos_sync ? SyncPolarity::Positive : SyncPolarity::Negative); | ||
219 | |||
220 | if (sm[14].matched) { | ||
221 | for (int i = 0; i < sm[14].length(); ++i) { | ||
222 | char f = string(sm[14])[i]; | ||
223 | |||
224 | switch (f) { | ||
225 | case 'i': | ||
226 | output.mode.set_interlace(true); | ||
227 | break; | ||
228 | default: | ||
229 | EXIT("Bad mode flag %c", f); | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | } else { | ||
234 | EXIT("Failed to parse crtc option '%s'", crtc_str.c_str()); | ||
235 | } | ||
236 | |||
237 | if (!resman.reserve_crtc(output.crtc)) | ||
238 | EXIT("Could not find available crtc"); | ||
239 | } | ||
240 | |||
241 | static void parse_plane(ResourceManager& resman, Card& card, const string& plane_str, const OutputInfo& output, PlaneInfo& pinfo) | ||
242 | { | ||
243 | // 3:400,400-400x400 | ||
244 | const regex plane_re("(?:(@?)(\\d+):)?" // 3: | ||
245 | "(?:(\\d+),(\\d+)-)?" // 400,400- | ||
246 | "(\\d+)x(\\d+)"); // 400x400 | ||
247 | |||
248 | smatch sm; | ||
249 | if (!regex_match(plane_str, sm, plane_re)) | ||
250 | EXIT("Failed to parse plane option '%s'", plane_str.c_str()); | ||
251 | |||
252 | if (sm[2].matched) { | ||
253 | bool use_id = sm[1].length() == 1; | ||
254 | unsigned num = stoul(sm[2].str()); | ||
255 | |||
256 | if (use_id) { | ||
257 | Plane* p = card.get_plane(num); | ||
258 | if (!p) | ||
259 | EXIT("Bad plane id '%u'", num); | ||
260 | |||
261 | pinfo.plane = p; | ||
262 | } else { | ||
263 | auto planes = card.get_planes(); | ||
264 | |||
265 | if (num >= planes.size()) | ||
266 | EXIT("Bad plane number '%u'", num); | ||
267 | |||
268 | pinfo.plane = planes[num]; | ||
269 | } | ||
270 | |||
271 | auto plane = resman.reserve_plane(pinfo.plane); | ||
272 | if (!plane) | ||
273 | EXIT("Plane id %u is not available", pinfo.plane->id()); | ||
274 | } | ||
275 | |||
276 | pinfo.w = stoul(sm[5]); | ||
277 | pinfo.h = stoul(sm[6]); | ||
278 | |||
279 | if (sm[3].matched) | ||
280 | pinfo.x = stoul(sm[3]); | ||
281 | else | ||
282 | pinfo.x = output.mode.hdisplay / 2 - pinfo.w / 2; | ||
283 | |||
284 | if (sm[4].matched) | ||
285 | pinfo.y = stoul(sm[4]); | ||
286 | else | ||
287 | pinfo.y = output.mode.vdisplay / 2 - pinfo.h / 2; | ||
288 | } | ||
289 | |||
290 | static void parse_prop(const string& prop_str, vector<PropInfo> &props) | ||
291 | { | ||
292 | string name, val; | ||
293 | |||
294 | size_t split = prop_str.find("="); | ||
295 | |||
296 | if (split == string::npos) | ||
297 | EXIT("Equal sign ('=') not found in %s", prop_str.c_str()); | ||
298 | |||
299 | name = prop_str.substr(0, split); | ||
300 | val = prop_str.substr(split+1); | ||
301 | |||
302 | props.push_back(PropInfo(name, stoull(val, 0, 0))); | ||
303 | } | ||
304 | |||
305 | static void get_props(Card& card, vector<PropInfo> &props, const DrmPropObject* propobj) | ||
306 | { | ||
307 | for (auto& pi : props) | ||
308 | pi.prop = propobj->get_prop(pi.name); | ||
309 | } | ||
310 | |||
311 | static vector<Framebuffer*> get_default_fb(Card& card, unsigned width, unsigned height) | ||
312 | { | ||
313 | vector<Framebuffer*> v; | ||
314 | |||
315 | for (unsigned i = 0; i < s_num_buffers; ++i) | ||
316 | v.push_back(new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888)); | ||
317 | |||
318 | return v; | ||
319 | } | ||
320 | |||
321 | static void parse_fb(Card& card, const string& fb_str, OutputInfo* output, PlaneInfo* pinfo) | ||
322 | { | ||
323 | unsigned w, h; | ||
324 | PixelFormat format = PixelFormat::XRGB8888; | ||
325 | |||
326 | if (pinfo) { | ||
327 | w = pinfo->w; | ||
328 | h = pinfo->h; | ||
329 | } else { | ||
330 | w = output->mode.hdisplay; | ||
331 | h = output->mode.vdisplay; | ||
332 | } | ||
333 | |||
334 | if (!fb_str.empty()) { | ||
335 | // XXX the regexp is not quite correct | ||
336 | // 400x400-NV12 | ||
337 | const regex fb_re("(?:(\\d+)x(\\d+))?" // 400x400 | ||
338 | "(?:-)?" // - | ||
339 | "(\\w\\w\\w\\w)?"); // NV12 | ||
340 | |||
341 | smatch sm; | ||
342 | if (!regex_match(fb_str, sm, fb_re)) | ||
343 | EXIT("Failed to parse fb option '%s'", fb_str.c_str()); | ||
344 | |||
345 | if (sm[1].matched) | ||
346 | w = stoul(sm[1]); | ||
347 | if (sm[2].matched) | ||
348 | h = stoul(sm[2]); | ||
349 | if (sm[3].matched) | ||
350 | format = FourCCToPixelFormat(sm[3]); | ||
351 | } | ||
352 | |||
353 | vector<Framebuffer*> v; | ||
354 | |||
355 | for (unsigned i = 0; i < s_num_buffers; ++i) | ||
356 | v.push_back(new DumbFramebuffer(card, w, h, format)); | ||
357 | |||
358 | if (pinfo) | ||
359 | pinfo->fbs = v; | ||
360 | else | ||
361 | output->legacy_fbs = v; | ||
362 | } | ||
363 | |||
364 | static void parse_view(const string& view_str, PlaneInfo& pinfo) | ||
365 | { | ||
366 | const regex view_re("(\\d+),(\\d+)-(\\d+)x(\\d+)"); // 400,400-400x400 | ||
367 | |||
368 | smatch sm; | ||
369 | if (!regex_match(view_str, sm, view_re)) | ||
370 | EXIT("Failed to parse view option '%s'", view_str.c_str()); | ||
371 | |||
372 | pinfo.view_x = stoul(sm[1]); | ||
373 | pinfo.view_y = stoul(sm[2]); | ||
374 | pinfo.view_w = stoul(sm[3]); | ||
375 | pinfo.view_h = stoul(sm[4]); | ||
376 | } | ||
377 | |||
378 | static const char* usage_str = | ||
379 | "Usage: kmstest [OPTION]...\n\n" | ||
380 | "Show a test pattern on a display or plane\n\n" | ||
381 | "Options:\n" | ||
382 | " --device=DEVICE DEVICE is the path to DRM card to open\n" | ||
383 | " -c, --connector=CONN CONN is <connector>\n" | ||
384 | " -r, --crtc=CRTC CRTC is [<crtc>:]<w>x<h>[@<Hz>]\n" | ||
385 | " or\n" | ||
386 | " [<crtc>:]<pclk>,<hact>/<hfp>/<hsw>/<hbp>/<hsp>,<vact>/<vfp>/<vsw>/<vbp>/<vsp>[,i]\n" | ||
387 | " -p, --plane=PLANE PLANE is [<plane>:][<x>,<y>-]<w>x<h>\n" | ||
388 | " -f, --fb=FB FB is [<w>x<h>][-][<4cc>]\n" | ||
389 | " -v, --view=VIEW VIEW is <x>,<y>-<w>x<h>\n" | ||
390 | " -P, --property=PROP=VAL Set PROP to VAL in the previous DRM object\n" | ||
391 | " --dmt Search for the given mode from DMT tables\n" | ||
392 | " --cea Search for the given mode from CEA tables\n" | ||
393 | " --cvt=CVT Create videomode with CVT. CVT is 'v1', 'v2' or 'v2o'\n" | ||
394 | " --flip[=max] Do page flipping for each output with an optional maximum flips count\n" | ||
395 | " --sync Synchronize page flipping\n" | ||
396 | "\n" | ||
397 | "<connector>, <crtc> and <plane> can be given by index (<idx>) or id (@<id>).\n" | ||
398 | "<connector> can also be given by name.\n" | ||
399 | "\n" | ||
400 | "Options can be given multiple times to set up multiple displays or planes.\n" | ||
401 | "Options may apply to previous options, e.g. a plane will be set on a crtc set in\n" | ||
402 | "an earlier option.\n" | ||
403 | "If you omit parameters, kmstest tries to guess what you mean\n" | ||
404 | "\n" | ||
405 | "Examples:\n" | ||
406 | "\n" | ||
407 | "Set eDP-1 mode to 1920x1080@60, show XR24 framebuffer on the crtc, and a 400x400 XB24 plane:\n" | ||
408 | " kmstest -c eDP-1 -r 1920x1080@60 -f XR24 -p 400x400 -f XB24\n\n" | ||
409 | "XR24 framebuffer on first connected connector in the default mode:\n" | ||
410 | " kmstest -f XR24\n\n" | ||
411 | "XR24 framebuffer on a 400x400 plane on the first connected connector in the default mode:\n" | ||
412 | " kmstest -p 400x400 -f XR24\n\n" | ||
413 | "Test pattern on the second connector with default mode:\n" | ||
414 | " kmstest -c 1\n" | ||
415 | "\n" | ||
416 | "Environmental variables:\n" | ||
417 | " KMSXX_DISABLE_UNIVERSAL_PLANES Don't enable universal planes even if available\n" | ||
418 | " KMSXX_DISABLE_ATOMIC Don't enable atomic modesetting even if available\n" | ||
419 | ; | ||
420 | |||
421 | static void usage() | ||
422 | { | ||
423 | puts(usage_str); | ||
424 | } | ||
425 | |||
426 | enum class ArgType | ||
427 | { | ||
428 | Connector, | ||
429 | Crtc, | ||
430 | Plane, | ||
431 | Framebuffer, | ||
432 | View, | ||
433 | Property, | ||
434 | }; | ||
435 | |||
436 | struct Arg | ||
437 | { | ||
438 | ArgType type; | ||
439 | string arg; | ||
440 | }; | ||
441 | |||
442 | static string s_device_path = "/dev/dri/card0"; | ||
443 | |||
444 | static vector<Arg> parse_cmdline(int argc, char **argv) | ||
445 | { | ||
446 | vector<Arg> args; | ||
447 | |||
448 | OptionSet optionset = { | ||
449 | Option("|device=", | ||
450 | [&](string s) | ||
451 | { | ||
452 | s_device_path = s; | ||
453 | }), | ||
454 | Option("c|connector=", | ||
455 | [&](string s) | ||
456 | { | ||
457 | args.push_back(Arg { ArgType::Connector, s }); | ||
458 | }), | ||
459 | Option("r|crtc=", [&](string s) | ||
460 | { | ||
461 | args.push_back(Arg { ArgType::Crtc, s }); | ||
462 | }), | ||
463 | Option("p|plane=", [&](string s) | ||
464 | { | ||
465 | args.push_back(Arg { ArgType::Plane, s }); | ||
466 | }), | ||
467 | Option("f|fb=", [&](string s) | ||
468 | { | ||
469 | args.push_back(Arg { ArgType::Framebuffer, s }); | ||
470 | }), | ||
471 | Option("v|view=", [&](string s) | ||
472 | { | ||
473 | args.push_back(Arg { ArgType::View, s }); | ||
474 | }), | ||
475 | Option("P|property=", [&](string s) | ||
476 | { | ||
477 | args.push_back(Arg { ArgType::Property, s }); | ||
478 | }), | ||
479 | Option("|dmt", []() | ||
480 | { | ||
481 | s_use_dmt = true; | ||
482 | }), | ||
483 | Option("|cea", []() | ||
484 | { | ||
485 | s_use_cea = true; | ||
486 | }), | ||
487 | Option("|flip?", [&](string s) | ||
488 | { | ||
489 | s_flip_mode = true; | ||
490 | s_num_buffers = 2; | ||
491 | if (!s.empty()) | ||
492 | s_max_flips = stoi(s); | ||
493 | }), | ||
494 | Option("|sync", []() | ||
495 | { | ||
496 | s_flip_sync = true; | ||
497 | }), | ||
498 | Option("|cvt=", [&](string s) | ||
499 | { | ||
500 | if (s == "v1") | ||
501 | s_cvt = true; | ||
502 | else if (s == "v2") | ||
503 | s_cvt = s_cvt_v2 = true; | ||
504 | else if (s == "v2o") | ||
505 | s_cvt = s_cvt_v2 = s_cvt_vid_opt = true; | ||
506 | else { | ||
507 | usage(); | ||
508 | exit(-1); | ||
509 | } | ||
510 | }), | ||
511 | Option("h|help", [&]() | ||
512 | { | ||
513 | usage(); | ||
514 | exit(-1); | ||
515 | }), | ||
516 | }; | ||
517 | |||
518 | optionset.parse(argc, argv); | ||
519 | |||
520 | if (optionset.params().size() > 0) { | ||
521 | usage(); | ||
522 | exit(-1); | ||
523 | } | ||
524 | |||
525 | return args; | ||
526 | } | ||
527 | |||
528 | static vector<OutputInfo> setups_to_outputs(Card& card, ResourceManager& resman, const vector<Arg>& output_args) | ||
529 | { | ||
530 | vector<OutputInfo> outputs; | ||
531 | |||
532 | OutputInfo* current_output = 0; | ||
533 | PlaneInfo* current_plane = 0; | ||
534 | |||
535 | for (auto& arg : output_args) { | ||
536 | switch (arg.type) { | ||
537 | case ArgType::Connector: | ||
538 | { | ||
539 | outputs.push_back(OutputInfo { }); | ||
540 | current_output = &outputs.back(); | ||
541 | |||
542 | get_connector(resman, *current_output, arg.arg); | ||
543 | current_plane = 0; | ||
544 | |||
545 | break; | ||
546 | } | ||
547 | |||
548 | case ArgType::Crtc: | ||
549 | { | ||
550 | if (!current_output) { | ||
551 | outputs.push_back(OutputInfo { }); | ||
552 | current_output = &outputs.back(); | ||
553 | } | ||
554 | |||
555 | if (!current_output->connector) | ||
556 | get_connector(resman, *current_output); | ||
557 | |||
558 | parse_crtc(resman, card, arg.arg, *current_output); | ||
559 | |||
560 | current_plane = 0; | ||
561 | |||
562 | break; | ||
563 | } | ||
564 | |||
565 | case ArgType::Plane: | ||
566 | { | ||
567 | if (!current_output) { | ||
568 | outputs.push_back(OutputInfo { }); | ||
569 | current_output = &outputs.back(); | ||
570 | } | ||
571 | |||
572 | if (!current_output->connector) | ||
573 | get_connector(resman, *current_output); | ||
574 | |||
575 | if (!current_output->crtc) | ||
576 | get_default_crtc(resman, *current_output); | ||
577 | |||
578 | current_plane = add_default_planeinfo(current_output); | ||
579 | |||
580 | parse_plane(resman, card, arg.arg, *current_output, *current_plane); | ||
581 | |||
582 | break; | ||
583 | } | ||
584 | |||
585 | case ArgType::Framebuffer: | ||
586 | { | ||
587 | if (!current_output) { | ||
588 | outputs.push_back(OutputInfo { }); | ||
589 | current_output = &outputs.back(); | ||
590 | } | ||
591 | |||
592 | if (!current_output->connector) | ||
593 | get_connector(resman, *current_output); | ||
594 | |||
595 | if (!current_output->crtc) | ||
596 | get_default_crtc(resman, *current_output); | ||
597 | |||
598 | if (!current_plane && card.has_atomic()) | ||
599 | current_plane = add_default_planeinfo(current_output); | ||
600 | |||
601 | parse_fb(card, arg.arg, current_output, current_plane); | ||
602 | |||
603 | break; | ||
604 | } | ||
605 | |||
606 | case ArgType::View: | ||
607 | { | ||
608 | if (!current_plane || current_plane->fbs.empty()) | ||
609 | EXIT("'view' parameter requires a plane and a fb"); | ||
610 | |||
611 | parse_view(arg.arg, *current_plane); | ||
612 | break; | ||
613 | } | ||
614 | |||
615 | case ArgType::Property: | ||
616 | { | ||
617 | if (!current_output) | ||
618 | EXIT("No object to which set the property"); | ||
619 | |||
620 | if (current_plane) | ||
621 | parse_prop(arg.arg, current_plane->props); | ||
622 | else if (current_output->crtc) | ||
623 | parse_prop(arg.arg, current_output->crtc_props); | ||
624 | else if (current_output->connector) | ||
625 | parse_prop(arg.arg, current_output->conn_props); | ||
626 | else | ||
627 | EXIT("no object"); | ||
628 | |||
629 | break; | ||
630 | } | ||
631 | } | ||
632 | } | ||
633 | |||
634 | if (outputs.empty()) { | ||
635 | // no outputs defined, show a pattern on all screens | ||
636 | for (Connector* conn : card.get_connectors()) { | ||
637 | if (!conn->connected()) | ||
638 | continue; | ||
639 | |||
640 | OutputInfo output = { }; | ||
641 | output.connector = resman.reserve_connector(conn); | ||
642 | EXIT_IF(!output.connector, "Failed to reserve connector %s", conn->fullname().c_str()); | ||
643 | output.crtc = resman.reserve_crtc(conn); | ||
644 | EXIT_IF(!output.crtc, "Failed to reserve crtc for %s", conn->fullname().c_str()); | ||
645 | output.mode = output.connector->get_default_mode(); | ||
646 | |||
647 | outputs.push_back(output); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | for (OutputInfo& o : outputs) { | ||
652 | get_props(card, o.conn_props, o.connector); | ||
653 | |||
654 | if (!o.crtc) | ||
655 | get_default_crtc(resman, o); | ||
656 | |||
657 | get_props(card, o.crtc_props, o.crtc); | ||
658 | |||
659 | if (card.has_atomic()) { | ||
660 | if (o.planes.empty()) | ||
661 | add_default_planeinfo(&o); | ||
662 | } else { | ||
663 | if (o.legacy_fbs.empty()) | ||
664 | o.legacy_fbs = get_default_fb(card, o.mode.hdisplay, o.mode.vdisplay); | ||
665 | } | ||
666 | |||
667 | for (PlaneInfo &p : o.planes) { | ||
668 | if (p.fbs.empty()) | ||
669 | p.fbs = get_default_fb(card, p.w, p.h); | ||
670 | } | ||
671 | |||
672 | for (PlaneInfo& p : o.planes) { | ||
673 | if (!p.plane) { | ||
674 | if (card.has_atomic()) | ||
675 | p.plane = resman.reserve_generic_plane(o.crtc, p.fbs[0]->format()); | ||
676 | else | ||
677 | p.plane = resman.reserve_overlay_plane(o.crtc, p.fbs[0]->format()); | ||
678 | |||
679 | if (!p.plane) | ||
680 | EXIT("Failed to find available plane"); | ||
681 | } | ||
682 | get_props(card, p.props, p.plane); | ||
683 | } | ||
684 | } | ||
685 | |||
686 | return outputs; | ||
687 | } | ||
688 | |||
689 | static std::string videomode_to_string(const Videomode& m) | ||
690 | { | ||
691 | string h = sformat("%u/%u/%u/%u", m.hdisplay, m.hfp(), m.hsw(), m.hbp()); | ||
692 | string v = sformat("%u/%u/%u/%u", m.vdisplay, m.vfp(), m.vsw(), m.vbp()); | ||
693 | |||
694 | return sformat("%s %.3f %s %s %u (%.2f) %#x %#x", | ||
695 | m.name.c_str(), | ||
696 | m.clock / 1000.0, | ||
697 | h.c_str(), v.c_str(), | ||
698 | m.vrefresh, m.calculated_vrefresh(), | ||
699 | m.flags, | ||
700 | m.type); | ||
701 | } | ||
702 | |||
703 | static void print_outputs(const vector<OutputInfo>& outputs) | ||
704 | { | ||
705 | for (unsigned i = 0; i < outputs.size(); ++i) { | ||
706 | const OutputInfo& o = outputs[i]; | ||
707 | |||
708 | printf("Connector %u/@%u: %s", o.connector->idx(), o.connector->id(), | ||
709 | o.connector->fullname().c_str()); | ||
710 | |||
711 | for (const PropInfo &prop: o.conn_props) | ||
712 | printf(" %s=%" PRIu64, prop.prop->name().c_str(), | ||
713 | prop.val); | ||
714 | |||
715 | printf("\n Crtc %u/@%u", o.crtc->idx(), o.crtc->id()); | ||
716 | |||
717 | for (const PropInfo &prop: o.crtc_props) | ||
718 | printf(" %s=%" PRIu64, prop.prop->name().c_str(), | ||
719 | prop.val); | ||
720 | |||
721 | printf(": %s\n", videomode_to_string(o.mode).c_str()); | ||
722 | |||
723 | if (!o.legacy_fbs.empty()) { | ||
724 | auto fb = o.legacy_fbs[0]; | ||
725 | printf(" (Fb %u %ux%u-%s)", fb->id(), fb->width(), fb->height(), PixelFormatToFourCC(fb->format()).c_str()); | ||
726 | } | ||
727 | |||
728 | for (unsigned j = 0; j < o.planes.size(); ++j) { | ||
729 | const PlaneInfo& p = o.planes[j]; | ||
730 | auto fb = p.fbs[0]; | ||
731 | printf(" Plane %u/@%u: %u,%u-%ux%u", p.plane->idx(), p.plane->id(), | ||
732 | p.x, p.y, p.w, p.h); | ||
733 | for (const PropInfo &prop: p.props) | ||
734 | printf(" %s=%" PRIu64, prop.prop->name().c_str(), | ||
735 | prop.val); | ||
736 | printf("\n"); | ||
737 | |||
738 | printf(" Fb %u %ux%u-%s\n", fb->id(), fb->width(), fb->height(), | ||
739 | PixelFormatToFourCC(fb->format()).c_str()); | ||
740 | } | ||
741 | } | ||
742 | } | ||
743 | |||
744 | static void draw_test_patterns(const vector<OutputInfo>& outputs) | ||
745 | { | ||
746 | for (const OutputInfo& o : outputs) { | ||
747 | for (auto fb : o.legacy_fbs) | ||
748 | draw_test_pattern(*fb); | ||
749 | |||
750 | for (const PlaneInfo& p : o.planes) | ||
751 | for (auto fb : p.fbs) | ||
752 | draw_test_pattern(*fb); | ||
753 | } | ||
754 | } | ||
755 | |||
756 | static void set_crtcs_n_planes_legacy(Card& card, const vector<OutputInfo>& outputs) | ||
757 | { | ||
758 | // Disable unused crtcs | ||
759 | for (Crtc* crtc : card.get_crtcs()) { | ||
760 | if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end()) | ||
761 | continue; | ||
762 | |||
763 | crtc->disable_mode(); | ||
764 | } | ||
765 | |||
766 | for (const OutputInfo& o : outputs) { | ||
767 | auto conn = o.connector; | ||
768 | auto crtc = o.crtc; | ||
769 | |||
770 | if (!o.conn_props.empty() || !o.crtc_props.empty()) | ||
771 | printf("WARNING: properties not set without atomic modesetting"); | ||
772 | |||
773 | if (!o.legacy_fbs.empty()) { | ||
774 | auto fb = o.legacy_fbs[0]; | ||
775 | int r = crtc->set_mode(conn, *fb, o.mode); | ||
776 | if (r) | ||
777 | printf("crtc->set_mode() failed for crtc %u: %s\n", | ||
778 | crtc->id(), strerror(-r)); | ||
779 | } | ||
780 | |||
781 | for (const PlaneInfo& p : o.planes) { | ||
782 | auto fb = p.fbs[0]; | ||
783 | int r = crtc->set_plane(p.plane, *fb, | ||
784 | p.x, p.y, p.w, p.h, | ||
785 | 0, 0, fb->width(), fb->height()); | ||
786 | if (r) | ||
787 | printf("crtc->set_plane() failed for plane %u: %s\n", | ||
788 | p.plane->id(), strerror(-r)); | ||
789 | if (!p.props.empty()) | ||
790 | printf("WARNING: properties not set without atomic modesetting"); | ||
791 | } | ||
792 | } | ||
793 | } | ||
794 | |||
795 | static void set_crtcs_n_planes_atomic(Card& card, const vector<OutputInfo>& outputs) | ||
796 | { | ||
797 | int r; | ||
798 | |||
799 | // XXX DRM framework doesn't allow moving an active plane from one crtc to another. | ||
800 | // See drm_atomic.c::plane_switching_crtc(). | ||
801 | // For the time being, disable all crtcs and planes here. | ||
802 | |||
803 | AtomicReq disable_req(card); | ||
804 | |||
805 | // Disable unused crtcs | ||
806 | for (Crtc* crtc : card.get_crtcs()) { | ||
807 | //if (find_if(outputs.begin(), outputs.end(), [crtc](const OutputInfo& o) { return o.crtc == crtc; }) != outputs.end()) | ||
808 | // continue; | ||
809 | |||
810 | disable_req.add(crtc, { | ||
811 | { "ACTIVE", 0 }, | ||
812 | }); | ||
813 | } | ||
814 | |||
815 | // Disable unused planes | ||
816 | for (Plane* plane : card.get_planes()) | ||
817 | disable_req.add(plane, { | ||
818 | { "FB_ID", 0 }, | ||
819 | { "CRTC_ID", 0 }, | ||
820 | }); | ||
821 | |||
822 | r = disable_req.commit_sync(true); | ||
823 | if (r) | ||
824 | EXIT("Atomic commit failed when disabling: %d\n", r); | ||
825 | |||
826 | |||
827 | // Keep blobs here so that we keep ref to them until we have committed the req | ||
828 | vector<unique_ptr<Blob>> blobs; | ||
829 | |||
830 | AtomicReq req(card); | ||
831 | |||
832 | for (const OutputInfo& o : outputs) { | ||
833 | auto conn = o.connector; | ||
834 | auto crtc = o.crtc; | ||
835 | |||
836 | blobs.emplace_back(o.mode.to_blob(card)); | ||
837 | Blob* mode_blob = blobs.back().get(); | ||
838 | |||
839 | req.add(conn, { | ||
840 | { "CRTC_ID", crtc->id() }, | ||
841 | }); | ||
842 | |||
843 | for (const PropInfo &prop: o.conn_props) | ||
844 | req.add(conn, prop.prop, prop.val); | ||
845 | |||
846 | req.add(crtc, { | ||
847 | { "ACTIVE", 1 }, | ||
848 | { "MODE_ID", mode_blob->id() }, | ||
849 | }); | ||
850 | |||
851 | for (const PropInfo &prop: o.crtc_props) | ||
852 | req.add(crtc, prop.prop, prop.val); | ||
853 | |||
854 | for (const PlaneInfo& p : o.planes) { | ||
855 | auto fb = p.fbs[0]; | ||
856 | |||
857 | req.add(p.plane, { | ||
858 | { "FB_ID", fb->id() }, | ||
859 | { "CRTC_ID", crtc->id() }, | ||
860 | { "SRC_X", (p.view_x ?: 0) << 16 }, | ||
861 | { "SRC_Y", (p.view_y ?: 0) << 16 }, | ||
862 | { "SRC_W", (p.view_w ?: fb->width()) << 16 }, | ||
863 | { "SRC_H", (p.view_h ?: fb->height()) << 16 }, | ||
864 | { "CRTC_X", p.x }, | ||
865 | { "CRTC_Y", p.y }, | ||
866 | { "CRTC_W", p.w }, | ||
867 | { "CRTC_H", p.h }, | ||
868 | }); | ||
869 | |||
870 | for (const PropInfo &prop: p.props) | ||
871 | req.add(p.plane, prop.prop, prop.val); | ||
872 | } | ||
873 | } | ||
874 | |||
875 | r = req.test(true); | ||
876 | if (r) | ||
877 | EXIT("Atomic test failed: %d\n", r); | ||
878 | |||
879 | r = req.commit_sync(true); | ||
880 | if (r) | ||
881 | EXIT("Atomic commit failed: %d\n", r); | ||
882 | } | ||
883 | |||
884 | static void set_crtcs_n_planes(Card& card, const vector<OutputInfo>& outputs) | ||
885 | { | ||
886 | if (card.has_atomic()) | ||
887 | set_crtcs_n_planes_atomic(card, outputs); | ||
888 | else | ||
889 | set_crtcs_n_planes_legacy(card, outputs); | ||
890 | } | ||
891 | |||
892 | static bool max_flips_reached; | ||
893 | |||
894 | class FlipState : private PageFlipHandlerBase | ||
895 | { | ||
896 | public: | ||
897 | FlipState(Card& card, const string& name, vector<const OutputInfo*> outputs) | ||
898 | : m_card(card), m_name(name), m_outputs(outputs) | ||
899 | { | ||
900 | } | ||
901 | |||
902 | void start_flipping() | ||
903 | { | ||
904 | m_prev_frame = m_prev_print = std::chrono::steady_clock::now(); | ||
905 | m_slowest_frame = std::chrono::duration<float>::min(); | ||
906 | m_frame_num = 0; | ||
907 | queue_next(); | ||
908 | } | ||
909 | |||
910 | private: | ||
911 | void handle_page_flip(uint32_t frame, double time) | ||
912 | { | ||
913 | /* | ||
914 | * We get flip event for each crtc in this flipstate. We can commit the next frames | ||
915 | * only after we've gotten the flip event for all crtcs | ||
916 | */ | ||
917 | if (++m_flip_count < m_outputs.size()) | ||
918 | return; | ||
919 | |||
920 | m_frame_num++; | ||
921 | if (s_max_flips && m_frame_num >= s_max_flips) | ||
922 | max_flips_reached = true; | ||
923 | |||
924 | auto now = std::chrono::steady_clock::now(); | ||
925 | |||
926 | std::chrono::duration<float> diff = now - m_prev_frame; | ||
927 | if (diff > m_slowest_frame) | ||
928 | m_slowest_frame = diff; | ||
929 | |||
930 | if (m_frame_num % 100 == 0) { | ||
931 | std::chrono::duration<float> fsec = now - m_prev_print; | ||
932 | printf("Connector %s: fps %f, slowest %.2f ms\n", | ||
933 | m_name.c_str(), | ||
934 | 100.0 / fsec.count(), | ||
935 | m_slowest_frame.count() * 1000); | ||
936 | m_prev_print = now; | ||
937 | m_slowest_frame = std::chrono::duration<float>::min(); | ||
938 | } | ||
939 | |||
940 | m_prev_frame = now; | ||
941 | |||
942 | queue_next(); | ||
943 | } | ||
944 | |||
945 | static unsigned get_bar_pos(Framebuffer* fb, unsigned frame_num) | ||
946 | { | ||
947 | return (frame_num * bar_speed) % (fb->width() - bar_width + 1); | ||
948 | } | ||
949 | |||
950 | static void draw_bar(Framebuffer* fb, unsigned frame_num) | ||
951 | { | ||
952 | int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers); | ||
953 | int new_xpos = get_bar_pos(fb, frame_num); | ||
954 | |||
955 | draw_color_bar(*fb, old_xpos, new_xpos, bar_width); | ||
956 | draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); | ||
957 | } | ||
958 | |||
959 | static void do_flip_output(AtomicReq& req, unsigned frame_num, const OutputInfo& o) | ||
960 | { | ||
961 | unsigned cur = frame_num % s_num_buffers; | ||
962 | |||
963 | for (const PlaneInfo& p : o.planes) { | ||
964 | auto fb = p.fbs[cur]; | ||
965 | |||
966 | draw_bar(fb, frame_num); | ||
967 | |||
968 | req.add(p.plane, { | ||
969 | { "FB_ID", fb->id() }, | ||
970 | }); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | void do_flip_output_legacy(unsigned frame_num, const OutputInfo& o) | ||
975 | { | ||
976 | unsigned cur = frame_num % s_num_buffers; | ||
977 | |||
978 | if (!o.legacy_fbs.empty()) { | ||
979 | auto fb = o.legacy_fbs[cur]; | ||
980 | |||
981 | draw_bar(fb, frame_num); | ||
982 | |||
983 | int r = o.crtc->page_flip(*fb, this); | ||
984 | ASSERT(r == 0); | ||
985 | } | ||
986 | |||
987 | for (const PlaneInfo& p : o.planes) { | ||
988 | auto fb = p.fbs[cur]; | ||
989 | |||
990 | draw_bar(fb, frame_num); | ||
991 | |||
992 | int r = o.crtc->set_plane(p.plane, *fb, | ||
993 | p.x, p.y, p.w, p.h, | ||
994 | 0, 0, fb->width(), fb->height()); | ||
995 | ASSERT(r == 0); | ||
996 | } | ||
997 | } | ||
998 | |||
999 | void queue_next() | ||
1000 | { | ||
1001 | m_flip_count = 0; | ||
1002 | |||
1003 | if (m_card.has_atomic()) { | ||
1004 | AtomicReq req(m_card); | ||
1005 | |||
1006 | for (auto o : m_outputs) | ||
1007 | do_flip_output(req, m_frame_num, *o); | ||
1008 | |||
1009 | int r = req.commit(this); | ||
1010 | if (r) | ||
1011 | EXIT("Flip commit failed: %d\n", r); | ||
1012 | } else { | ||
1013 | ASSERT(m_outputs.size() == 1); | ||
1014 | do_flip_output_legacy(m_frame_num, *m_outputs[0]); | ||
1015 | } | ||
1016 | } | ||
1017 | |||
1018 | Card& m_card; | ||
1019 | string m_name; | ||
1020 | vector<const OutputInfo*> m_outputs; | ||
1021 | unsigned m_frame_num; | ||
1022 | unsigned m_flip_count; | ||
1023 | |||
1024 | chrono::steady_clock::time_point m_prev_print; | ||
1025 | chrono::steady_clock::time_point m_prev_frame; | ||
1026 | chrono::duration<float> m_slowest_frame; | ||
1027 | |||
1028 | static const unsigned bar_width = 20; | ||
1029 | static const unsigned bar_speed = 8; | ||
1030 | }; | ||
1031 | |||
1032 | static void main_flip(Card& card, const vector<OutputInfo>& outputs) | ||
1033 | { | ||
1034 | fd_set fds; | ||
1035 | |||
1036 | FD_ZERO(&fds); | ||
1037 | |||
1038 | int fd = card.fd(); | ||
1039 | |||
1040 | vector<unique_ptr<FlipState>> flipstates; | ||
1041 | |||
1042 | if (!s_flip_sync) { | ||
1043 | for (const OutputInfo& o : outputs) { | ||
1044 | auto fs = unique_ptr<FlipState>(new FlipState(card, to_string(o.connector->idx()), { &o })); | ||
1045 | flipstates.push_back(move(fs)); | ||
1046 | } | ||
1047 | } else { | ||
1048 | vector<const OutputInfo*> ois; | ||
1049 | |||
1050 | string name; | ||
1051 | for (const OutputInfo& o : outputs) { | ||
1052 | name += to_string(o.connector->idx()) + ","; | ||
1053 | ois.push_back(&o); | ||
1054 | } | ||
1055 | |||
1056 | auto fs = unique_ptr<FlipState>(new FlipState(card, name, ois)); | ||
1057 | flipstates.push_back(move(fs)); | ||
1058 | } | ||
1059 | |||
1060 | for (unique_ptr<FlipState>& fs : flipstates) | ||
1061 | fs->start_flipping(); | ||
1062 | |||
1063 | while (!max_flips_reached) { | ||
1064 | int r; | ||
1065 | |||
1066 | FD_SET(0, &fds); | ||
1067 | FD_SET(fd, &fds); | ||
1068 | |||
1069 | r = select(fd + 1, &fds, NULL, NULL, NULL); | ||
1070 | if (r < 0) { | ||
1071 | fprintf(stderr, "select() failed with %d: %m\n", errno); | ||
1072 | break; | ||
1073 | } else if (FD_ISSET(0, &fds)) { | ||
1074 | fprintf(stderr, "Exit due to user-input\n"); | ||
1075 | break; | ||
1076 | } else if (FD_ISSET(fd, &fds)) { | ||
1077 | card.call_page_flip_handlers(); | ||
1078 | } | ||
1079 | } | ||
1080 | } | ||
1081 | |||
1082 | int main(int argc, char **argv) | ||
1083 | { | ||
1084 | vector<Arg> output_args = parse_cmdline(argc, argv); | ||
1085 | |||
1086 | Card card(s_device_path); | ||
1087 | |||
1088 | if (!card.has_atomic() && s_flip_sync) | ||
1089 | EXIT("Synchronized flipping requires atomic modesetting"); | ||
1090 | |||
1091 | ResourceManager resman(card); | ||
1092 | |||
1093 | vector<OutputInfo> outputs = setups_to_outputs(card, resman, output_args); | ||
1094 | |||
1095 | if (!s_flip_mode) | ||
1096 | draw_test_patterns(outputs); | ||
1097 | |||
1098 | print_outputs(outputs); | ||
1099 | |||
1100 | set_crtcs_n_planes(card, outputs); | ||
1101 | |||
1102 | printf("press enter to exit\n"); | ||
1103 | |||
1104 | if (s_flip_mode) | ||
1105 | main_flip(card, outputs); | ||
1106 | else | ||
1107 | getchar(); | ||
1108 | } | ||
diff --git a/utils/kmsview.cpp b/utils/kmsview.cpp new file mode 100644 index 0000000..04d005d --- /dev/null +++ b/utils/kmsview.cpp | |||
@@ -0,0 +1,123 @@ | |||
1 | #include <cstdio> | ||
2 | #include <fstream> | ||
3 | #include <unistd.h> | ||
4 | |||
5 | #include <kms++/kms++.h> | ||
6 | #include <kms++util/kms++util.h> | ||
7 | |||
8 | using namespace std; | ||
9 | using namespace kms; | ||
10 | |||
11 | static void read_frame(ifstream& is, DumbFramebuffer* fb, Crtc* crtc, Plane* plane) | ||
12 | { | ||
13 | for (unsigned i = 0; i < fb->num_planes(); ++i) | ||
14 | is.read((char*)fb->map(i), fb->size(i)); | ||
15 | |||
16 | unsigned w = min(crtc->width(), fb->width()); | ||
17 | unsigned h = min(crtc->height(), fb->height()); | ||
18 | |||
19 | int r = crtc->set_plane(plane, *fb, | ||
20 | 0, 0, w, h, | ||
21 | 0, 0, fb->width(), fb->height()); | ||
22 | |||
23 | ASSERT(r == 0); | ||
24 | } | ||
25 | |||
26 | static const char* usage_str = | ||
27 | "Usage: kmsview [options] <file> <width> <height> <fourcc>\n\n" | ||
28 | "Options:\n" | ||
29 | " -c, --connector <name> Output connector\n" | ||
30 | " -t, --time <ms> Milliseconds to sleep between frames\n" | ||
31 | ; | ||
32 | |||
33 | static void usage() | ||
34 | { | ||
35 | puts(usage_str); | ||
36 | } | ||
37 | |||
38 | int main(int argc, char** argv) | ||
39 | { | ||
40 | uint32_t time = 0; | ||
41 | string dev_path = "/dev/dri/card0"; | ||
42 | string conn_name; | ||
43 | |||
44 | OptionSet optionset = { | ||
45 | Option("c|connector=", [&conn_name](string s) | ||
46 | { | ||
47 | conn_name = s; | ||
48 | }), | ||
49 | Option("|device=", [&dev_path](string s) | ||
50 | { | ||
51 | dev_path = s; | ||
52 | }), | ||
53 | Option("t|time=", [&time](const string& str) | ||
54 | { | ||
55 | time = stoul(str); | ||
56 | }), | ||
57 | Option("h|help", []() | ||
58 | { | ||
59 | usage(); | ||
60 | exit(-1); | ||
61 | }), | ||
62 | }; | ||
63 | |||
64 | optionset.parse(argc, argv); | ||
65 | |||
66 | vector<string> params = optionset.params(); | ||
67 | |||
68 | if (params.size() != 4) { | ||
69 | usage(); | ||
70 | exit(-1); | ||
71 | } | ||
72 | |||
73 | string filename = params[0]; | ||
74 | uint32_t w = stoi(params[1]); | ||
75 | uint32_t h = stoi(params[2]); | ||
76 | string modestr = params[3]; | ||
77 | |||
78 | auto pixfmt = FourCCToPixelFormat(modestr); | ||
79 | |||
80 | ifstream is(filename, ifstream::binary); | ||
81 | |||
82 | is.seekg(0, std::ios::end); | ||
83 | unsigned fsize = is.tellg(); | ||
84 | is.seekg(0); | ||
85 | |||
86 | |||
87 | Card card(dev_path); | ||
88 | ResourceManager res(card); | ||
89 | |||
90 | auto conn = res.reserve_connector(conn_name); | ||
91 | auto crtc = res.reserve_crtc(conn); | ||
92 | auto plane = res.reserve_overlay_plane(crtc, pixfmt); | ||
93 | FAIL_IF(!plane, "available plane not found"); | ||
94 | |||
95 | auto fb = new DumbFramebuffer(card, w, h, pixfmt); | ||
96 | |||
97 | unsigned frame_size = 0; | ||
98 | for (unsigned i = 0; i < fb->num_planes(); ++i) | ||
99 | frame_size += fb->size(i); | ||
100 | |||
101 | unsigned num_frames = fsize / frame_size; | ||
102 | printf("file size %u, frame size %u, frames %u\n", fsize, frame_size, num_frames); | ||
103 | |||
104 | for (unsigned i = 0; i < num_frames; ++i) { | ||
105 | printf("frame %d", i); fflush(stdout); | ||
106 | read_frame(is, fb, crtc, plane); | ||
107 | if (!time) { | ||
108 | getchar(); | ||
109 | } else { | ||
110 | usleep(time * 1000); | ||
111 | printf("\n"); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | is.close(); | ||
116 | |||
117 | if (time) { | ||
118 | printf("press enter to exit\n"); | ||
119 | getchar(); | ||
120 | } | ||
121 | |||
122 | delete fb; | ||
123 | } | ||
diff --git a/utils/wbcap.cpp b/utils/wbcap.cpp new file mode 100644 index 0000000..5a94a70 --- /dev/null +++ b/utils/wbcap.cpp | |||
@@ -0,0 +1,420 @@ | |||
1 | #include <cstdio> | ||
2 | #include <poll.h> | ||
3 | #include <unistd.h> | ||
4 | #include <algorithm> | ||
5 | #include <fstream> | ||
6 | |||
7 | #include <kms++/kms++.h> | ||
8 | #include <kms++util/kms++util.h> | ||
9 | #include <kms++util/videodevice.h> | ||
10 | |||
11 | #define CAMERA_BUF_QUEUE_SIZE 5 | ||
12 | |||
13 | using namespace std; | ||
14 | using namespace kms; | ||
15 | |||
16 | static vector<DumbFramebuffer*> s_fbs; | ||
17 | static vector<DumbFramebuffer*> s_free_fbs; | ||
18 | static vector<DumbFramebuffer*> s_wb_fbs; | ||
19 | static vector<DumbFramebuffer*> s_ready_fbs; | ||
20 | |||
21 | class WBStreamer | ||
22 | { | ||
23 | public: | ||
24 | WBStreamer(VideoStreamer* streamer, Crtc* crtc, PixelFormat pixfmt) | ||
25 | : m_capdev(*streamer) | ||
26 | { | ||
27 | Videomode m = crtc->mode(); | ||
28 | |||
29 | m_capdev.set_port(crtc->idx()); | ||
30 | m_capdev.set_format(pixfmt, m.hdisplay, m.vdisplay / (m.interlace() ? 2 : 1)); | ||
31 | m_capdev.set_queue_size(s_fbs.size()); | ||
32 | |||
33 | for (auto fb : s_free_fbs) { | ||
34 | m_capdev.queue(fb); | ||
35 | s_wb_fbs.push_back(fb); | ||
36 | } | ||
37 | |||
38 | s_free_fbs.clear(); | ||
39 | } | ||
40 | |||
41 | ~WBStreamer() | ||
42 | { | ||
43 | } | ||
44 | |||
45 | WBStreamer(const WBStreamer& other) = delete; | ||
46 | WBStreamer& operator=(const WBStreamer& other) = delete; | ||
47 | |||
48 | int fd() const { return m_capdev.fd(); } | ||
49 | |||
50 | void start_streaming() | ||
51 | { | ||
52 | m_capdev.stream_on(); | ||
53 | } | ||
54 | |||
55 | void stop_streaming() | ||
56 | { | ||
57 | m_capdev.stream_off(); | ||
58 | } | ||
59 | |||
60 | DumbFramebuffer* Dequeue() | ||
61 | { | ||
62 | auto fb = m_capdev.dequeue(); | ||
63 | |||
64 | auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb); | ||
65 | s_wb_fbs.erase(iter); | ||
66 | |||
67 | s_ready_fbs.insert(s_ready_fbs.begin(), fb); | ||
68 | |||
69 | return fb; | ||
70 | } | ||
71 | |||
72 | void Queue() | ||
73 | { | ||
74 | if (s_free_fbs.size() == 0) | ||
75 | return; | ||
76 | |||
77 | auto fb = s_free_fbs.back(); | ||
78 | s_free_fbs.pop_back(); | ||
79 | |||
80 | m_capdev.queue(fb); | ||
81 | |||
82 | s_wb_fbs.insert(s_wb_fbs.begin(), fb); | ||
83 | } | ||
84 | |||
85 | private: | ||
86 | VideoStreamer& m_capdev; | ||
87 | }; | ||
88 | |||
89 | class WBFlipState : private PageFlipHandlerBase | ||
90 | { | ||
91 | public: | ||
92 | WBFlipState(Card& card, Crtc* crtc, Plane* plane) | ||
93 | : m_card(card), m_crtc(crtc), m_plane(plane) | ||
94 | { | ||
95 | auto fb = s_ready_fbs.back(); | ||
96 | s_ready_fbs.pop_back(); | ||
97 | |||
98 | AtomicReq req(m_card); | ||
99 | |||
100 | req.add(m_plane, "CRTC_ID", m_crtc->id()); | ||
101 | req.add(m_plane, "FB_ID", fb->id()); | ||
102 | |||
103 | req.add(m_plane, "CRTC_X", 0); | ||
104 | req.add(m_plane, "CRTC_Y", 0); | ||
105 | req.add(m_plane, "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width())); | ||
106 | req.add(m_plane, "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height())); | ||
107 | |||
108 | req.add(m_plane, "SRC_X", 0); | ||
109 | req.add(m_plane, "SRC_Y", 0); | ||
110 | req.add(m_plane, "SRC_W", fb->width() << 16); | ||
111 | req.add(m_plane, "SRC_H", fb->height() << 16); | ||
112 | |||
113 | int r = req.commit_sync(); | ||
114 | FAIL_IF(r, "initial plane setup failed"); | ||
115 | |||
116 | m_current_fb = fb; | ||
117 | } | ||
118 | |||
119 | void queue_next() | ||
120 | { | ||
121 | if (m_queued_fb) | ||
122 | return; | ||
123 | |||
124 | if (s_ready_fbs.size() == 0) | ||
125 | return; | ||
126 | |||
127 | auto fb = s_ready_fbs.back(); | ||
128 | s_ready_fbs.pop_back(); | ||
129 | |||
130 | AtomicReq req(m_card); | ||
131 | req.add(m_plane, "FB_ID", fb->id()); | ||
132 | |||
133 | int r = req.commit(this); | ||
134 | if (r) | ||
135 | EXIT("Flip commit failed: %d\n", r); | ||
136 | |||
137 | m_queued_fb = fb; | ||
138 | } | ||
139 | |||
140 | private: | ||
141 | void handle_page_flip(uint32_t frame, double time) | ||
142 | { | ||
143 | if (m_queued_fb) { | ||
144 | if (m_current_fb) | ||
145 | s_free_fbs.insert(s_free_fbs.begin(), m_current_fb); | ||
146 | |||
147 | m_current_fb = m_queued_fb; | ||
148 | m_queued_fb = nullptr; | ||
149 | } | ||
150 | |||
151 | queue_next(); | ||
152 | } | ||
153 | |||
154 | Card& m_card; | ||
155 | Crtc* m_crtc; | ||
156 | Plane* m_plane; | ||
157 | |||
158 | DumbFramebuffer* m_current_fb = nullptr; | ||
159 | DumbFramebuffer* m_queued_fb = nullptr; | ||
160 | }; | ||
161 | |||
162 | class BarFlipState : private PageFlipHandlerBase | ||
163 | { | ||
164 | public: | ||
165 | BarFlipState(Card& card, Crtc* crtc, Plane* plane, uint32_t width, uint32_t height) | ||
166 | : m_card(card), m_crtc(crtc), m_plane(plane) | ||
167 | { | ||
168 | for (unsigned i = 0; i < s_num_buffers; ++i) | ||
169 | m_fbs[i] = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888); | ||
170 | } | ||
171 | |||
172 | ~BarFlipState() | ||
173 | { | ||
174 | for (unsigned i = 0; i < s_num_buffers; ++i) | ||
175 | delete m_fbs[i]; | ||
176 | } | ||
177 | |||
178 | void start_flipping() | ||
179 | { | ||
180 | m_frame_num = 0; | ||
181 | queue_next(); | ||
182 | } | ||
183 | |||
184 | private: | ||
185 | void handle_page_flip(uint32_t frame, double time) | ||
186 | { | ||
187 | m_frame_num++; | ||
188 | queue_next(); | ||
189 | } | ||
190 | |||
191 | static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num) | ||
192 | { | ||
193 | return (frame_num * bar_speed) % (fb->width() - bar_width + 1); | ||
194 | } | ||
195 | |||
196 | void draw_bar(DumbFramebuffer* fb, unsigned frame_num) | ||
197 | { | ||
198 | int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers); | ||
199 | int new_xpos = get_bar_pos(fb, frame_num); | ||
200 | |||
201 | draw_color_bar(*fb, old_xpos, new_xpos, bar_width); | ||
202 | draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); | ||
203 | } | ||
204 | |||
205 | void queue_next() | ||
206 | { | ||
207 | AtomicReq req(m_card); | ||
208 | |||
209 | unsigned cur = m_frame_num % s_num_buffers; | ||
210 | |||
211 | auto fb = m_fbs[cur]; | ||
212 | |||
213 | draw_bar(fb, m_frame_num); | ||
214 | |||
215 | req.add(m_plane, { | ||
216 | { "CRTC_ID", m_crtc->id() }, | ||
217 | { "FB_ID", fb->id() }, | ||
218 | |||
219 | { "CRTC_X", 0 }, | ||
220 | { "CRTC_Y", 0 }, | ||
221 | { "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()) }, | ||
222 | { "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()) }, | ||
223 | |||
224 | { "SRC_X", 0 }, | ||
225 | { "SRC_Y", 0 }, | ||
226 | { "SRC_W", fb->width() << 16 }, | ||
227 | { "SRC_H", fb->height() << 16 }, | ||
228 | }); | ||
229 | |||
230 | int r = req.commit(this); | ||
231 | if (r) | ||
232 | EXIT("Flip commit failed: %d\n", r); | ||
233 | } | ||
234 | |||
235 | static const unsigned s_num_buffers = 3; | ||
236 | |||
237 | DumbFramebuffer* m_fbs[s_num_buffers]; | ||
238 | |||
239 | Card& m_card; | ||
240 | Crtc* m_crtc; | ||
241 | Plane* m_plane; | ||
242 | |||
243 | unsigned m_frame_num; | ||
244 | |||
245 | static const unsigned bar_width = 20; | ||
246 | static const unsigned bar_speed = 8; | ||
247 | }; | ||
248 | |||
249 | static const char* usage_str = | ||
250 | "Usage: wbcap [OPTIONS]\n\n" | ||
251 | "Options:\n" | ||
252 | " -s, --src=CONN Source connector\n" | ||
253 | " -d, --dst=CONN Destination connector\n" | ||
254 | " -m, --smode=MODE Source connector videomode\n" | ||
255 | " -M, --dmode=MODE Destination connector videomode\n" | ||
256 | " -f, --format=4CC Format\n" | ||
257 | " -w, --write Write captured frames to wbcap.raw file\n" | ||
258 | " -h, --help Print this help\n" | ||
259 | ; | ||
260 | |||
261 | int main(int argc, char** argv) | ||
262 | { | ||
263 | string src_conn_name; | ||
264 | string src_mode_name; | ||
265 | string dst_conn_name; | ||
266 | string dst_mode_name; | ||
267 | PixelFormat pixfmt = PixelFormat::XRGB8888; | ||
268 | bool write_file = false; | ||
269 | |||
270 | OptionSet optionset = { | ||
271 | Option("s|src=", [&](string s) | ||
272 | { | ||
273 | src_conn_name = s; | ||
274 | }), | ||
275 | Option("m|smode=", [&](string s) | ||
276 | { | ||
277 | src_mode_name = s; | ||
278 | }), | ||
279 | Option("d|dst=", [&](string s) | ||
280 | { | ||
281 | dst_conn_name = s; | ||
282 | }), | ||
283 | Option("M|dmode=", [&](string s) | ||
284 | { | ||
285 | dst_mode_name = s; | ||
286 | }), | ||
287 | Option("f|format=", [&](string s) | ||
288 | { | ||
289 | pixfmt = FourCCToPixelFormat(s); | ||
290 | }), | ||
291 | Option("w|write", [&]() | ||
292 | { | ||
293 | write_file = true; | ||
294 | }), | ||
295 | Option("h|help", [&]() | ||
296 | { | ||
297 | puts(usage_str); | ||
298 | exit(-1); | ||
299 | }), | ||
300 | }; | ||
301 | |||
302 | optionset.parse(argc, argv); | ||
303 | |||
304 | if (optionset.params().size() > 0) { | ||
305 | puts(usage_str); | ||
306 | exit(-1); | ||
307 | } | ||
308 | |||
309 | if (src_conn_name.empty()) | ||
310 | EXIT("No source connector defined"); | ||
311 | |||
312 | if (dst_conn_name.empty()) | ||
313 | EXIT("No destination connector defined"); | ||
314 | |||
315 | VideoDevice vid("/dev/video11"); | ||
316 | |||
317 | Card card; | ||
318 | ResourceManager resman(card); | ||
319 | |||
320 | card.disable_all(); | ||
321 | |||
322 | auto src_conn = resman.reserve_connector(src_conn_name); | ||
323 | auto src_crtc = resman.reserve_crtc(src_conn); | ||
324 | auto src_plane = resman.reserve_generic_plane(src_crtc, pixfmt); | ||
325 | FAIL_IF(!src_plane, "Plane not found"); | ||
326 | Videomode src_mode = src_mode_name.empty() ? src_conn->get_default_mode() : src_conn->get_mode(src_mode_name); | ||
327 | src_crtc->set_mode(src_conn, src_mode); | ||
328 | |||
329 | |||
330 | auto dst_conn = resman.reserve_connector(dst_conn_name); | ||
331 | auto dst_crtc = resman.reserve_crtc(dst_conn); | ||
332 | auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt); | ||
333 | FAIL_IF(!dst_plane, "Plane not found"); | ||
334 | Videomode dst_mode = dst_mode_name.empty() ? dst_conn->get_default_mode() : dst_conn->get_mode(dst_mode_name); | ||
335 | dst_crtc->set_mode(dst_conn, dst_mode); | ||
336 | |||
337 | uint32_t src_width = src_mode.hdisplay; | ||
338 | uint32_t src_height = src_mode.vdisplay; | ||
339 | |||
340 | uint32_t dst_width = src_mode.hdisplay; | ||
341 | uint32_t dst_height = src_mode.vdisplay; | ||
342 | if (src_mode.interlace()) | ||
343 | dst_height /= 2; | ||
344 | |||
345 | printf("src %s, crtc %s\n", src_conn->fullname().c_str(), src_mode.to_string().c_str()); | ||
346 | |||
347 | printf("dst %s, crtc %s\n", dst_conn->fullname().c_str(), dst_mode.to_string().c_str()); | ||
348 | |||
349 | printf("src_fb %ux%u, dst_fb %ux%u\n", src_width, src_height, dst_width, dst_height); | ||
350 | |||
351 | for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) { | ||
352 | auto fb = new DumbFramebuffer(card, dst_width, dst_height, pixfmt); | ||
353 | s_fbs.push_back(fb); | ||
354 | s_free_fbs.push_back(fb); | ||
355 | } | ||
356 | |||
357 | // get one fb for initial setup | ||
358 | s_ready_fbs.push_back(s_free_fbs.back()); | ||
359 | s_free_fbs.pop_back(); | ||
360 | |||
361 | // This draws a moving bar to SRC display | ||
362 | BarFlipState barflipper(card, src_crtc, src_plane, src_width, src_height); | ||
363 | barflipper.start_flipping(); | ||
364 | |||
365 | // This shows the captured SRC frames on DST display | ||
366 | WBFlipState wbflipper(card, dst_crtc, dst_plane); | ||
367 | |||
368 | WBStreamer wb(vid.get_capture_streamer(), src_crtc, pixfmt); | ||
369 | wb.start_streaming(); | ||
370 | |||
371 | vector<pollfd> fds(3); | ||
372 | |||
373 | fds[0].fd = 0; | ||
374 | fds[0].events = POLLIN; | ||
375 | fds[1].fd = wb.fd(); | ||
376 | fds[1].events = POLLIN; | ||
377 | fds[2].fd = card.fd(); | ||
378 | fds[2].events = POLLIN; | ||
379 | |||
380 | uint32_t dst_frame_num = 0; | ||
381 | |||
382 | const string filename = "wbcap.raw"; | ||
383 | unique_ptr<ofstream> os; | ||
384 | if (write_file) | ||
385 | os = unique_ptr<ofstream>(new ofstream(filename, ofstream::binary)); | ||
386 | |||
387 | while (true) { | ||
388 | int r = poll(fds.data(), fds.size(), -1); | ||
389 | ASSERT(r > 0); | ||
390 | |||
391 | if (fds[0].revents != 0) | ||
392 | break; | ||
393 | |||
394 | if (fds[1].revents) { | ||
395 | fds[1].revents = 0; | ||
396 | |||
397 | DumbFramebuffer* fb = wb.Dequeue(); | ||
398 | |||
399 | if (write_file) { | ||
400 | printf("Writing frame %u to %s\n", dst_frame_num, filename.c_str()); | ||
401 | |||
402 | for (unsigned i = 0; i < fb->num_planes(); ++i) | ||
403 | os->write((char*)fb->map(i), fb->size(i)); | ||
404 | |||
405 | dst_frame_num++; | ||
406 | } | ||
407 | |||
408 | wbflipper.queue_next(); | ||
409 | } | ||
410 | |||
411 | if (fds[2].revents) { | ||
412 | fds[2].revents = 0; | ||
413 | |||
414 | card.call_page_flip_handlers(); | ||
415 | wb.Queue(); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | printf("exiting...\n"); | ||
420 | } | ||
diff --git a/utils/wbm2m.cpp b/utils/wbm2m.cpp new file mode 100644 index 0000000..4a126a8 --- /dev/null +++ b/utils/wbm2m.cpp | |||
@@ -0,0 +1,169 @@ | |||
1 | #include <cstdio> | ||
2 | #include <poll.h> | ||
3 | #include <unistd.h> | ||
4 | #include <algorithm> | ||
5 | #include <fstream> | ||
6 | #include <map> | ||
7 | #include <system_error> | ||
8 | |||
9 | #include <kms++/kms++.h> | ||
10 | #include <kms++util/kms++util.h> | ||
11 | #include <kms++util/videodevice.h> | ||
12 | |||
13 | const uint32_t NUM_SRC_BUFS=2; | ||
14 | const uint32_t NUM_DST_BUFS=2; | ||
15 | |||
16 | using namespace std; | ||
17 | using namespace kms; | ||
18 | |||
19 | static const char* usage_str = | ||
20 | "Usage: wbm2m [OPTIONS]\n\n" | ||
21 | "Options:\n" | ||
22 | " -f, --format=4CC Output format" | ||
23 | " -h, --help Print this help\n" | ||
24 | ; | ||
25 | |||
26 | const int bar_speed = 4; | ||
27 | const int bar_width = 10; | ||
28 | |||
29 | static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num) | ||
30 | { | ||
31 | return (frame_num * bar_speed) % (fb->width() - bar_width + 1); | ||
32 | } | ||
33 | |||
34 | static void read_frame(DumbFramebuffer* fb, unsigned frame_num) | ||
35 | { | ||
36 | static map<DumbFramebuffer*, int> s_bar_pos_map; | ||
37 | |||
38 | int old_pos = -1; | ||
39 | if (s_bar_pos_map.find(fb) != s_bar_pos_map.end()) | ||
40 | old_pos = s_bar_pos_map[fb]; | ||
41 | |||
42 | int pos = get_bar_pos(fb, frame_num); | ||
43 | draw_color_bar(*fb, old_pos, pos, bar_width); | ||
44 | draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); | ||
45 | s_bar_pos_map[fb] = pos; | ||
46 | } | ||
47 | |||
48 | int main(int argc, char** argv) | ||
49 | { | ||
50 | // XXX get from args | ||
51 | const uint32_t src_width = 800; | ||
52 | const uint32_t src_height = 480; | ||
53 | const auto src_fmt = PixelFormat::XRGB8888; | ||
54 | const uint32_t num_src_frames = 10; | ||
55 | |||
56 | const uint32_t dst_width = 800; | ||
57 | const uint32_t dst_height = 480; | ||
58 | auto dst_fmt = PixelFormat::XRGB8888; | ||
59 | |||
60 | const string filename = "wb-out.raw"; | ||
61 | |||
62 | OptionSet optionset = { | ||
63 | Option("f|format=", [&](string s) | ||
64 | { | ||
65 | dst_fmt = FourCCToPixelFormat(s); | ||
66 | }), | ||
67 | Option("h|help", [&]() | ||
68 | { | ||
69 | puts(usage_str); | ||
70 | exit(-1); | ||
71 | }), | ||
72 | }; | ||
73 | |||
74 | optionset.parse(argc, argv); | ||
75 | |||
76 | if (optionset.params().size() > 0) { | ||
77 | puts(usage_str); | ||
78 | exit(-1); | ||
79 | } | ||
80 | |||
81 | VideoDevice vid("/dev/video10"); | ||
82 | |||
83 | Card card; | ||
84 | |||
85 | uint32_t src_frame_num = 0; | ||
86 | uint32_t dst_frame_num = 0; | ||
87 | |||
88 | VideoStreamer* out = vid.get_output_streamer(); | ||
89 | VideoStreamer* in = vid.get_capture_streamer(); | ||
90 | |||
91 | out->set_format(src_fmt, src_width, src_height); | ||
92 | in->set_format(dst_fmt, dst_width, dst_height); | ||
93 | |||
94 | out->set_queue_size(NUM_SRC_BUFS); | ||
95 | in->set_queue_size(NUM_DST_BUFS); | ||
96 | |||
97 | |||
98 | for (unsigned i = 0; i < min(NUM_SRC_BUFS, num_src_frames); ++i) { | ||
99 | auto fb = new DumbFramebuffer(card, src_width, src_height, src_fmt); | ||
100 | |||
101 | read_frame(fb, src_frame_num++); | ||
102 | |||
103 | out->queue(fb); | ||
104 | } | ||
105 | |||
106 | for (unsigned i = 0; i < min(NUM_DST_BUFS, num_src_frames); ++i) { | ||
107 | auto fb = new DumbFramebuffer(card, dst_width, dst_height, dst_fmt); | ||
108 | in->queue(fb); | ||
109 | } | ||
110 | |||
111 | vector<pollfd> fds(3); | ||
112 | |||
113 | fds[0].fd = 0; | ||
114 | fds[0].events = POLLIN; | ||
115 | fds[1].fd = vid.fd(); | ||
116 | fds[1].events = POLLIN; | ||
117 | fds[2].fd = card.fd(); | ||
118 | fds[2].events = POLLIN; | ||
119 | |||
120 | ofstream os(filename, ofstream::binary); | ||
121 | |||
122 | out->stream_on(); | ||
123 | in->stream_on(); | ||
124 | |||
125 | while (true) { | ||
126 | int r = poll(fds.data(), fds.size(), -1); | ||
127 | ASSERT(r > 0); | ||
128 | |||
129 | if (fds[0].revents != 0) | ||
130 | break; | ||
131 | |||
132 | if (fds[1].revents) { | ||
133 | fds[1].revents = 0; | ||
134 | |||
135 | |||
136 | try { | ||
137 | DumbFramebuffer *dst_fb = in->dequeue(); | ||
138 | printf("Writing frame %u\n", dst_frame_num); | ||
139 | for (unsigned i = 0; i < dst_fb->num_planes(); ++i) | ||
140 | os.write((char*)dst_fb->map(i), dst_fb->size(i)); | ||
141 | in->queue(dst_fb); | ||
142 | |||
143 | dst_frame_num++; | ||
144 | |||
145 | if (dst_frame_num >= num_src_frames) | ||
146 | break; | ||
147 | |||
148 | } catch (system_error& se) { | ||
149 | if (se.code() != errc::resource_unavailable_try_again) | ||
150 | FAIL("dequeue failed: %s", se.what()); | ||
151 | |||
152 | break; | ||
153 | } | ||
154 | |||
155 | DumbFramebuffer *src_fb = out->dequeue(); | ||
156 | |||
157 | if (src_frame_num < num_src_frames) { | ||
158 | read_frame(src_fb, src_frame_num++); | ||
159 | out->queue(src_fb); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | if (fds[2].revents) { | ||
164 | fds[2].revents = 0; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | printf("exiting...\n"); | ||
169 | } | ||