├── .drone.yml
├── CMakeLists.txt
├── LICENSE
├── README.md
├── drm.c
├── drm.h
├── drm_master.c
├── drm_master.h
├── input.c
├── input.h
├── keymap.c
├── keymap.h
├── kmsvnc.c
├── kmsvnc.h
├── va.c
└── va.h
/.drone.yml:
--------------------------------------------------------------------------------
1 | kind: pipeline
2 | type: docker
3 | name: default
4 |
5 | steps:
6 | - name: build
7 | image: archlinux:latest
8 | commands:
9 | - pacman -Syu --noconfirm --needed base-devel libvncserver libxkbcommon libdrm libva git cmake clang
10 | - export CFLAGS="-pipe -fno-plt -fexceptions -fstack-clash-protection -fcf-protection -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security"
11 | - CC=gcc cmake -B gcc-out
12 | - cmake --build gcc-out
13 | - CC=clang cmake -B clang-out
14 | - cmake --build clang-out
15 |
16 | trigger:
17 | branch:
18 | - dev
19 | event:
20 | exclude:
21 | - pull_request
22 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13)
2 | project(kmsvnc LANGUAGES C)
3 |
4 | IF(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
5 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
6 | endif()
7 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
8 |
9 | find_package(PkgConfig REQUIRED)
10 | pkg_search_module(LIBDRM REQUIRED libdrm)
11 | pkg_search_module(LIBVNCSERVER REQUIRED libvncserver)
12 | pkg_search_module(XKBCOMMON REQUIRED xkbcommon)
13 | pkg_search_module(LIBVA REQUIRED libva)
14 | pkg_search_module(LIBVA_DRM REQUIRED libva-drm)
15 |
16 | add_executable(kmsvnc)
17 | set(kmsvnc_SOURCES kmsvnc.c drm.c input.c keymap.c va.c drm_master.c)
18 |
19 | include(CheckIncludeFiles)
20 | CHECK_INCLUDE_FILES("linux/uinput.h;linux/dma-buf.h" HAVE_LINUX_API_HEADERS)
21 | IF(NOT HAVE_LINUX_API_HEADERS)
22 | message(FATAL_ERROR "linux-api-headers not found")
23 | ENDIF()
24 |
25 | include(CheckSymbolExists)
26 | check_symbol_exists(SYS_pidfd_getfd "sys/syscall.h" HAVE_LIBC_SYS_pidfd_getfd)
27 | IF(NOT HAVE_LIBC_SYS_pidfd_getfd)
28 | message(WARNING "pidfd_getfd syscall not found, the --screen-blank options will be disabled")
29 | target_compile_options(kmsvnc PUBLIC -DDISABLE_KMSVNC_SCREEN_BLANK)
30 | list(REMOVE_ITEM kmsvnc_SOURCES drm_master.c)
31 | ENDIF()
32 | include(CMakePushCheckState)
33 | cmake_push_check_state()
34 | set(CMAKE_REQUIRED_INCLUDES ${LIBDRM_INCLUDE_DIRS})
35 | set(CMAKE_REQUIRED_LIBRARIES ${LIBDRM_LIBRARIES})
36 | check_symbol_exists(drmGetFormatName "xf86drm.h" HAVE_LIBDRM_drmGetFormatName)
37 | cmake_pop_check_state()
38 | IF(NOT HAVE_LIBDRM_drmGetFormatName)
39 | message(WARNING "drmGetFormatName not found, format name printing will be disabled")
40 | target_compile_options(kmsvnc PUBLIC -DDISABLE_KMSVNC_drmGetFormatName)
41 | ENDIF()
42 |
43 |
44 | target_sources(kmsvnc PUBLIC
45 | ${kmsvnc_SOURCES}
46 | )
47 | target_include_directories(kmsvnc PUBLIC
48 | ${LIBDRM_INCLUDE_DIRS}
49 | ${LIBVNCSERVER_INCLUDE_DIRS}
50 | ${XKBCOMMON_INCLUDE_DIRS}
51 | ${LIBVA_INCLUDE_DIRS}
52 | ${LIBVA_DRM_INCLUDE_DIRS}
53 | )
54 | target_link_libraries(kmsvnc PUBLIC
55 | m
56 | ${LIBDRM_LIBRARIES}
57 | ${LIBVNCSERVER_LIBRARIES}
58 | ${XKBCOMMON_LIBRARIES}
59 | ${LIBVA_LIBRARIES}
60 | ${LIBVA_DRM_LIBRARIES}
61 | )
62 | install(TARGETS kmsvnc RUNTIME DESTINATION bin)
63 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kmsvnc
2 |
3 | [](https://drone.jerryxiao.cc/Jerry/kmsvnc)
4 |
5 | ## Introduction
6 | A VNC server for DRM/KMS capable GNU/Linux devices.
7 | The goal is to simply have a universally working vncserver on X, wayland and even something like your linux VT.
8 | Currently in very early development stage.
9 |
10 | ## Notes
11 | Intel made a great thing called CCS (Color Control Surface), however that won't work with kmsvnc. Please set `INTEL_DEBUG=noccs` globally, ideally in /etc/systemd/system.conf.d. Manpage is at `man 5 systemd-system.conf`. For example:
12 | ```
13 | # /etc/systemd/system.conf.d/intel-no-ccs.conf
14 | [Manager]
15 | DefaultEnvironment=INTEL_DEBUG=noccs
16 | ```
17 | NixOS:
18 | ```
19 | systemd.extraConfig = ''
20 | DefaultEnvironment=INTEL_DEBUG=noccs
21 | ''
22 | ```
23 |
24 | If you plan to use the default vaapi driver for Intel and AMD GPUs, please make sure your vaapi configuration is working.
25 | Nvidia support is highly experimental (nvidia-legacy with drm enabled or nvidia-open). Only one X-TILED modifier is supported as of now.
26 |
27 | ## Dependencies
28 | * cmake
29 | * libvncserver
30 | * libxkbcommon
31 | * libdrm
32 | * libva
33 |
34 | ## Building
35 | ```
36 | mkdir build
37 | cd build
38 | cmake ..
39 | make
40 | ```
41 |
42 | ## Running
43 | Helps are available via `kmsvnc --help`.
44 | For example, `kmsvnc -p 5901 -b 0.0.0.0 -4 -d /dev/dri/card2`
45 | Note that no security is currently supported.
46 |
--------------------------------------------------------------------------------
/drm.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "drm.h"
12 | #include "va.h"
13 |
14 | #ifndef DISABLE_KMSVNC_SCREEN_BLANK
15 | #include "drm_master.h"
16 | #endif
17 |
18 | #ifndef fourcc_mod_is_vendor
19 | #define fourcc_mod_is_vendor(modifier, vendor) \
20 | (fourcc_mod_get_vendor(modifier) == DRM_FORMAT_MOD_VENDOR_## vendor)
21 | #endif
22 | #ifdef DISABLE_KMSVNC_drmGetFormatName
23 | static char* drmGetFormatName(uint32_t data) {
24 | char *name = "missing drmGetFormatName";
25 | char *out = malloc(strlen(name)+1);
26 | if (out) {
27 | memcpy(out, name, strlen(name)+1);
28 | }
29 | return out;
30 | }
31 | #endif
32 |
33 | extern struct kmsvnc_data *kmsvnc;
34 |
35 | static int check_pixfmt_non_vaapi() {
36 | if (
37 | kmsvnc->drm->mfb->pixel_format != KMSVNC_FOURCC_TO_INT('X', 'R', '2', '4') &&
38 | kmsvnc->drm->mfb->pixel_format != KMSVNC_FOURCC_TO_INT('A', 'R', '2', '4')
39 | )
40 | {
41 | KMSVNC_FATAL("Unsupported pixfmt %s, please create an issue with your pixfmt.\n", kmsvnc->drm->pixfmt_name);
42 | }
43 | return 0;
44 | }
45 |
46 | static void convert_copy(const char *in, int width, int height, char *buff)
47 | {
48 | if (likely(in != buff)) {
49 | memcpy(buff, in, width * height * BYTES_PER_PIXEL);
50 | }
51 | }
52 |
53 | static void convert_bgra_to_rgba(const char *in, int width, int height, char *buff)
54 | {
55 | if (likely(in != buff)) {
56 | memcpy(buff, in, width * height * BYTES_PER_PIXEL);
57 | }
58 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
59 | uint32_t pixdata = htonl(*((uint32_t*)(buff + i)));
60 | buff[i+0] = (pixdata & 0x0000ff00) >> 8;
61 | buff[i+2] = (pixdata & 0xff000000) >> 24;
62 | }
63 | }
64 |
65 | static inline char convert_buf_allocate(size_t len) {
66 | if (kmsvnc->drm->kms_convert_buf_len < len)
67 | {
68 | if (kmsvnc->drm->kms_convert_buf)
69 | free(kmsvnc->drm->kms_convert_buf);
70 | kmsvnc->drm->kms_convert_buf = malloc(len);
71 | if (!kmsvnc->drm->kms_convert_buf) return 1;
72 | kmsvnc->drm->kms_convert_buf_len = len;
73 | }
74 | return 0;
75 | }
76 | static inline void convert_x_tiled(const int tilex, const int tiley, const char *in, int width, int height, char *buff)
77 | {
78 | if (width % tilex)
79 | {
80 | return;
81 | }
82 | if (height % tiley)
83 | {
84 | int sno = (width / tilex) + (height / tiley) * (width / tilex);
85 | int ord = (width % tilex) + (height % tiley) * tilex;
86 | int max_offset = sno * tilex * tiley + ord;
87 | if (kmsvnc->drm->kms_cpy_tmp_buf_len < max_offset * 4 + 4)
88 | {
89 | if (kmsvnc->drm->kms_cpy_tmp_buf)
90 | free(kmsvnc->drm->kms_convert_buf);
91 | kmsvnc->drm->kms_cpy_tmp_buf = malloc(max_offset * 4 + 4);
92 | if (!kmsvnc->drm->kms_cpy_tmp_buf) return;
93 | kmsvnc->drm->kms_cpy_tmp_buf_len = max_offset * 4 + 4;
94 | }
95 | memcpy(kmsvnc->drm->kms_cpy_tmp_buf, in, max_offset * 4 + 4);
96 | in = (const char *)kmsvnc->drm->kms_cpy_tmp_buf;
97 | }
98 | if (convert_buf_allocate(width * height * 4)) return;
99 | for (int y = 0; y < height; y++)
100 | {
101 | for (int x = 0; x < width; x++)
102 | {
103 | int sno = (x / tilex) + (y / tiley) * (width / tilex);
104 | int ord = (x % tilex) + (y % tiley) * tilex;
105 | int offset = sno * tilex * tiley + ord;
106 | memcpy(kmsvnc->drm->kms_convert_buf + (x + y * width) * 4, in + offset * 4, 4);
107 | }
108 | }
109 | convert_bgra_to_rgba(kmsvnc->drm->kms_convert_buf, width, height, buff);
110 | }
111 |
112 | void convert_nvidia_x_tiled_kmsbuf(const char *in, int width, int height, char *buff)
113 | {
114 | convert_x_tiled(16, 128, in, width, height, buff);
115 | }
116 | void convert_intel_x_tiled_kmsbuf(const char *in, int width, int height, char *buff)
117 | {
118 | convert_x_tiled(128, 8, in, width, height, buff);
119 | }
120 |
121 | static void convert_vaapi(const char *in, int width, int height, char *buff) {
122 | va_hwframe_to_vaapi(buff);
123 | if (
124 | (KMSVNC_FOURCC_TO_INT('R','G','B',0) & kmsvnc->va->selected_fmt->fourcc) == KMSVNC_FOURCC_TO_INT('R','G','B',0)
125 | ) {}
126 | else {
127 | // is 30 depth?
128 | if (kmsvnc->va->selected_fmt->depth == 30) {
129 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
130 | // ensure little endianess
131 | uint32_t pixdata = __builtin_bswap32(htonl(*((uint32_t*)(buff + i))));
132 | buff[i] = (pixdata & 0x3ff00000) >> 20 >> 2;
133 | buff[i+1] = (pixdata & 0xffc00) >> 10 >> 2;
134 | buff[i+2] = (pixdata & 0x3ff) >> 2;
135 | }
136 | }
137 | else {
138 | // actually, does anyone use this?
139 | if (!kmsvnc->va->selected_fmt->byte_order) {
140 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
141 | uint32_t *pixdata = (uint32_t*)(buff + i);
142 | *pixdata = __builtin_bswap32(*pixdata);
143 | }
144 | }
145 | }
146 | // is xrgb?
147 | if ((kmsvnc->va->selected_fmt->blue_mask | kmsvnc->va->selected_fmt->red_mask) < 0x1000000) {
148 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
149 | uint32_t *pixdata = (uint32_t*)(buff + i);
150 | *pixdata = ntohl(htonl(*pixdata) << 8);
151 | }
152 | }
153 | // is bgrx?
154 | if (kmsvnc->va->selected_fmt->blue_mask > kmsvnc->va->selected_fmt->red_mask) {
155 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
156 | uint32_t pixdata = htonl(*((uint32_t*)(buff + i)));
157 | buff[i+0] = (pixdata & 0x0000ff00) >> 8;
158 | buff[i+2] = (pixdata & 0xff000000) >> 24;
159 | }
160 | }
161 | }
162 | }
163 |
164 | static inline void drm_sync(int drmfd, uint64_t flags)
165 | {
166 | struct dma_buf_sync sync = {
167 | .flags = flags,
168 | };
169 | DRM_R_IOCTL_MAY(drmfd, DMA_BUF_IOCTL_SYNC, &sync);
170 | }
171 |
172 | void drm_sync_start(int drmfd)
173 | {
174 | drm_sync(drmfd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ);
175 | }
176 | void drm_sync_end(int drmfd)
177 | {
178 | drm_sync(drmfd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ);
179 | }
180 | void drm_sync_noop(int drmfd)
181 | {
182 | }
183 |
184 | void drm_cleanup() {
185 | if (kmsvnc->drm) {
186 | #ifndef DISABLE_KMSVNC_SCREEN_BLANK
187 | if (kmsvnc->drm->gamma && kmsvnc->drm->gamma->size && kmsvnc->drm->gamma->red && kmsvnc->drm->gamma->green && kmsvnc->drm->gamma->blue) {
188 | if (drmModeCrtcSetGamma(kmsvnc->drm->drm_master_fd ?: kmsvnc->drm->drm_fd, kmsvnc->drm->plane->crtc_id, kmsvnc->drm->gamma->size, kmsvnc->drm->gamma->red, kmsvnc->drm->gamma->green, kmsvnc->drm->gamma->blue)) perror("Failed to restore gamma");
189 | }
190 | if (kmsvnc->drm->gamma && kmsvnc->drm->gamma->red) {
191 | free(kmsvnc->drm->gamma->red);
192 | kmsvnc->drm->gamma->red = kmsvnc->drm->gamma->green = kmsvnc->drm->gamma->blue = NULL;
193 | }
194 | if (kmsvnc->drm->gamma) {
195 | free(kmsvnc->drm->gamma);
196 | kmsvnc->drm->gamma = NULL;
197 | }
198 | #endif
199 | if (kmsvnc->drm->drm_ver) {
200 | drmFreeVersion(kmsvnc->drm->drm_ver);
201 | kmsvnc->drm->drm_ver = NULL;
202 | }
203 | if (kmsvnc->drm->pixfmt_name) {
204 | free(kmsvnc->drm->pixfmt_name);
205 | kmsvnc->drm->pixfmt_name = NULL;
206 | }
207 | if (kmsvnc->drm->mod_vendor) {
208 | free(kmsvnc->drm->mod_vendor);
209 | kmsvnc->drm->mod_vendor = NULL;
210 | }
211 | if (kmsvnc->drm->mod_name) {
212 | free(kmsvnc->drm->mod_name);
213 | kmsvnc->drm->mod_name = NULL;
214 | }
215 | if (kmsvnc->drm->plane) {
216 | drmModeFreePlane(kmsvnc->drm->plane);
217 | kmsvnc->drm->plane = NULL;
218 | }
219 | if (kmsvnc->drm->cursor_plane) {
220 | drmModeFreePlane(kmsvnc->drm->cursor_plane);
221 | kmsvnc->drm->cursor_plane = NULL;
222 | }
223 | if (kmsvnc->drm->mfb) {
224 | drmModeFreeFB2(kmsvnc->drm->mfb);
225 | kmsvnc->drm->mfb = NULL;
226 | }
227 | if (kmsvnc->drm->cursor_mfb) {
228 | drmModeFreeFB2(kmsvnc->drm->cursor_mfb);
229 | kmsvnc->drm->cursor_mfb = NULL;
230 | }
231 | if (kmsvnc->drm->mapped && kmsvnc->drm->mapped != MAP_FAILED) {
232 | munmap(kmsvnc->drm->mapped, kmsvnc->drm->mmap_size);
233 | kmsvnc->drm->mapped = NULL;
234 | }
235 | if (kmsvnc->drm->cursor_mapped && kmsvnc->drm->cursor_mapped != MAP_FAILED) {
236 | munmap(kmsvnc->drm->cursor_mapped, kmsvnc->drm->cursor_mmap_size);
237 | kmsvnc->drm->cursor_mapped = NULL;
238 | }
239 | if (kmsvnc->drm->prime_fd > 0) {
240 | close(kmsvnc->drm->prime_fd);
241 | kmsvnc->drm->prime_fd = 0;
242 | }
243 | if (kmsvnc->drm->drm_fd > 0) {
244 | close(kmsvnc->drm->drm_fd);
245 | kmsvnc->drm->drm_fd = 0;
246 | }
247 | if (kmsvnc->drm->drm_master_fd > 0) {
248 | close(kmsvnc->drm->drm_master_fd);
249 | kmsvnc->drm->drm_master_fd = 0;
250 | }
251 | if (kmsvnc->drm->plane_res) {
252 | drmModeFreePlaneResources(kmsvnc->drm->plane_res);
253 | kmsvnc->drm->plane_res = NULL;
254 | }
255 | if (kmsvnc->drm->kms_convert_buf) {
256 | free(kmsvnc->drm->kms_convert_buf);
257 | kmsvnc->drm->kms_convert_buf = NULL;
258 | }
259 | kmsvnc->drm->kms_convert_buf_len = 0;
260 | if (kmsvnc->drm->kms_cpy_tmp_buf) {
261 | free(kmsvnc->drm->kms_cpy_tmp_buf);
262 | kmsvnc->drm->kms_cpy_tmp_buf = NULL;
263 | }
264 | kmsvnc->drm->kms_cpy_tmp_buf_len = 0;
265 | if (kmsvnc->drm->kms_cursor_buf) {
266 | free(kmsvnc->drm->kms_cursor_buf);
267 | kmsvnc->drm->kms_cursor_buf = NULL;
268 | }
269 | kmsvnc->drm->kms_cursor_buf_len = 0;
270 | free(kmsvnc->drm);
271 | kmsvnc->drm = NULL;
272 | }
273 | }
274 |
275 | static const char* drm_get_plane_type_name(uint64_t plane_type) {
276 | switch (plane_type) {
277 | case DRM_PLANE_TYPE_OVERLAY:
278 | return "overlay";
279 | case DRM_PLANE_TYPE_PRIMARY:
280 | return "primary";
281 | case DRM_PLANE_TYPE_CURSOR:
282 | return "cursor";
283 | default:
284 | return "unknown";
285 | }
286 | };
287 |
288 | static int drm_refresh_planes(char first_time) {
289 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
290 | if (!drm->plane && kmsvnc->source_plane > 0)
291 | {
292 | drm->plane = drmModeGetPlane(drm->drm_fd, kmsvnc->source_plane);
293 | if (!drm->plane)
294 | KMSVNC_FATAL("Failed to get plane %d: %s\n", kmsvnc->source_plane, strerror(errno));
295 | if (drm->plane->fb_id == 0)
296 | fprintf(stderr, "Place %d does not have an attached framebuffer\n", kmsvnc->source_plane);
297 | }
298 | if (!drm->plane || (kmsvnc->capture_cursor && !drm->cursor_plane)) {
299 | drmModePlane *current_plane = NULL;
300 | if (drm->plane_res) {
301 | drmModeFreePlaneResources(kmsvnc->drm->plane_res);
302 | drm->plane_res = NULL;
303 | }
304 | drm->plane_res = drmModeGetPlaneResources(drm->drm_fd);
305 | if (!drm->plane_res)
306 | KMSVNC_FATAL("Failed to get plane resources: %s\n", strerror(errno));
307 | int i;
308 | for (i = 0; i < drm->plane_res->count_planes; i++)
309 | {
310 | current_plane = drmModeGetPlane(drm->drm_fd, drm->plane_res->planes[i]);
311 | if (!current_plane)
312 | {
313 | fprintf(stderr, "Failed to get plane %u: %s\n", drm->plane_res->planes[i], strerror(errno));
314 | continue;
315 | }
316 | // get plane type
317 | uint64_t plane_type = 114514;
318 | drmModeObjectPropertiesPtr plane_props = drmModeObjectGetProperties(drm->drm_fd, current_plane->plane_id, DRM_MODE_OBJECT_PLANE);
319 | if (!plane_props) {
320 | fprintf(stderr, "Failed to get plane prop %u: %s\n", drm->plane_res->planes[i], strerror(errno));
321 | }
322 | else {
323 | for (int i = 0; i < plane_props->count_props; i++) {
324 | drmModePropertyPtr plane_prop = drmModeGetProperty(drm->drm_fd, plane_props->props[i]);
325 | if (strcmp(plane_prop->name, "type") == 0) {
326 | plane_type = plane_props->prop_values[i];
327 | }
328 | drmModeFreeProperty(plane_prop);
329 | }
330 | drmModeFreeObjectProperties(plane_props);
331 | }
332 | assert(drm->plane_res->planes[i] == current_plane->plane_id);
333 | if (first_time) {
334 | printf("Plane %u CRTC %u FB %u Type %s\n", current_plane->plane_id, current_plane->crtc_id, current_plane->fb_id, drm_get_plane_type_name(plane_type));
335 | }
336 | // populate drm->plane and drm->cursor_plane
337 | char nofree = 0;
338 | if (current_plane->fb_id != 0) {
339 | if (!drm->plane) {
340 | if (kmsvnc->source_crtc == 0 || current_plane->crtc_id == kmsvnc->source_crtc) {
341 | nofree = 1;
342 | drm->plane = current_plane;
343 | }
344 | }
345 | // assume cursor plane is always after primary plane
346 | if (!drm->cursor_plane) {
347 | if (drm->plane && drm->plane->crtc_id == current_plane->crtc_id && plane_type == DRM_PLANE_TYPE_CURSOR) {
348 | nofree = 1;
349 | drm->cursor_plane = current_plane;
350 | }
351 | }
352 | }
353 | if ((!kmsvnc->capture_cursor || drm->cursor_plane) && drm->plane) {
354 | break;
355 | }
356 | if (!nofree) {
357 | drmModeFreePlane(current_plane);
358 | }
359 | current_plane = NULL;
360 | }
361 | if (!first_time) return 0;
362 | if (i == drm->plane_res->count_planes)
363 | {
364 | if (!drm->plane) {
365 | if (kmsvnc->source_crtc != 0)
366 | {
367 | KMSVNC_FATAL("No usable planes found on CRTC %d\n", kmsvnc->source_crtc);
368 | }
369 | else
370 | {
371 | KMSVNC_FATAL("No usable planes found\n");
372 | }
373 | }
374 | else if (!drm->cursor_plane) {
375 | fprintf(stderr, "No usable cursor plane found, cursor capture currently unavailable\n");
376 | }
377 | }
378 | printf("Using plane %u to locate framebuffers\n", drm->plane->plane_id);
379 | if (drm->cursor_plane) {
380 | printf("Using cursor plane %u\n", drm->cursor_plane->plane_id);
381 | }
382 | }
383 | return 0;
384 | }
385 |
386 | int drm_dump_cursor_plane(char **data, int *width, int *height) {
387 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
388 |
389 | if (!drm->cursor_plane) {
390 | drm_refresh_planes(0); // ignore error
391 | if (drm->cursor_plane) {
392 | printf("Using cursor plane %u\n", drm->cursor_plane->plane_id);
393 | }
394 | }
395 | else {
396 | uint32_t plane_id = drm->cursor_plane->plane_id;
397 | drmModeFreePlane(drm->cursor_plane);
398 | drm->cursor_plane = NULL;
399 | drm->cursor_plane = drmModeGetPlane(drm->drm_fd, plane_id);
400 | }
401 | if (!drm->cursor_plane) {
402 | data = NULL;
403 | return 1;
404 | }
405 | if (drm->cursor_mfb) drmModeFreeFB2(drm->cursor_mfb);
406 | drm->cursor_mfb = drmModeGetFB2(drm->drm_fd, drm->cursor_plane->fb_id);
407 | if (!drm->cursor_mfb) {
408 | KMSVNC_DEBUG("Cursor framebuffer missing\n");
409 | return 1;
410 | }
411 |
412 | if (drm->cursor_mfb->modifier != DRM_FORMAT_MOD_NONE && drm->cursor_mfb->modifier != DRM_FORMAT_MOD_LINEAR) {
413 | //kmsvnc->capture_cursor = 0;
414 | KMSVNC_DEBUG("Cursor plane modifier is not linear: %lu\n", drm->cursor_mfb->modifier);
415 | return 1;
416 | }
417 |
418 | if (
419 | drm->cursor_mfb->pixel_format != KMSVNC_FOURCC_TO_INT('A', 'R', '2', '4') &&
420 | drm->cursor_mfb->pixel_format != KMSVNC_FOURCC_TO_INT('A', 'R', '3', '0')
421 | )
422 | {
423 | //kmsvnc->capture_cursor = 0;
424 | char *fmtname = drmGetFormatName(drm->cursor_mfb->pixel_format);
425 | KMSVNC_DEBUG("Cursor plane pixel format unsupported (%u, %s)\n", drm->cursor_mfb->pixel_format, fmtname);
426 | free(fmtname);
427 | return 1;
428 | }
429 |
430 | struct drm_gem_flink flink;
431 | flink.handle = drm->cursor_mfb->handles[0];
432 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_FLINK, &flink);
433 |
434 | struct drm_gem_open open_arg;
435 | open_arg.name = flink.name;
436 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
437 |
438 | struct drm_mode_map_dumb mreq;
439 | memset(&mreq, 0, sizeof(mreq));
440 | mreq.handle = open_arg.handle;
441 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
442 |
443 | size_t mmap_size = open_arg.size;
444 | if (mmap_size != drm->cursor_mfb->width * drm->cursor_mfb->height * BYTES_PER_PIXEL) {
445 | KMSVNC_DEBUG("Cursor plane mmap_size != calculated size (%ld, %d)\n", mmap_size, drm->cursor_mfb->width * drm->cursor_mfb->height * BYTES_PER_PIXEL);
446 | return 1;
447 | }
448 |
449 | off_t mmap_offset = mreq.offset;
450 | if (drm->cursor_mapped && drm->cursor_mapped != MAP_FAILED) munmap(drm->cursor_mapped, drm->cursor_mmap_size);
451 | drm->cursor_mapped = mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, drm->drm_fd, mmap_offset);
452 | if (drm->cursor_mapped == MAP_FAILED)
453 | {
454 | KMSVNC_DEBUG("Failed to mmap cursor: %s\n", strerror(errno));
455 | return 1;
456 | }
457 | else
458 | {
459 | if (kmsvnc->drm->kms_cursor_buf_len < mmap_size)
460 | {
461 | if (kmsvnc->drm->kms_cursor_buf)
462 | free(kmsvnc->drm->kms_cursor_buf);
463 | kmsvnc->drm->kms_cursor_buf = malloc(mmap_size);
464 | if (!kmsvnc->drm->kms_cursor_buf) return 1;
465 | kmsvnc->drm->kms_cursor_buf_len = mmap_size;
466 | }
467 | memcpy(drm->kms_cursor_buf, drm->cursor_mapped, mmap_size);
468 | if (drm->cursor_mfb->pixel_format == KMSVNC_FOURCC_TO_INT('X', 'R', '3', '0') ||
469 | drm->cursor_mfb->pixel_format == KMSVNC_FOURCC_TO_INT('A', 'R', '3', '0'))
470 | {
471 | for (int i = 0; i < drm->cursor_mfb->width * drm->cursor_mfb->height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
472 | uint32_t pixdata = __builtin_bswap32(htonl(*((uint32_t*)(kmsvnc->drm->kms_cursor_buf + i))));
473 | kmsvnc->drm->kms_cursor_buf[i] = (pixdata & 0x3ff00000) >> 20 >> 2;
474 | kmsvnc->drm->kms_cursor_buf[i+1] = (pixdata & 0xffc00) >> 10 >> 2;
475 | kmsvnc->drm->kms_cursor_buf[i+2] = (pixdata & 0x3ff) >> 2;
476 | kmsvnc->drm->kms_cursor_buf[i+3] = (pixdata & 0xc0000000) >> 30 << 6;
477 | }
478 | }
479 | if (drm->cursor_mfb->pixel_format == KMSVNC_FOURCC_TO_INT('X', 'R', '2', '4') ||
480 | drm->cursor_mfb->pixel_format == KMSVNC_FOURCC_TO_INT('A', 'R', '2', '4'))
481 | {
482 | // bgra to rgba
483 | for (int i = 0; i < drm->cursor_mfb->width * drm->cursor_mfb->height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
484 | uint32_t pixdata = htonl(*((uint32_t*)(kmsvnc->drm->kms_cursor_buf + i)));
485 | kmsvnc->drm->kms_cursor_buf[i+0] = (pixdata & 0x0000ff00) >> 8;
486 | kmsvnc->drm->kms_cursor_buf[i+2] = (pixdata & 0xff000000) >> 24;
487 | }
488 | }
489 | *width = drm->cursor_mfb->width;
490 | *height = drm->cursor_mfb->height;
491 | *data = drm->kms_cursor_buf;
492 | }
493 | return 0;
494 | }
495 |
496 | int drm_open() {
497 | struct kmsvnc_drm_data *drm = malloc(sizeof(struct kmsvnc_drm_data));
498 | if (!drm) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
499 | memset(drm, 0, sizeof(struct kmsvnc_drm_data));
500 | kmsvnc->drm = drm;
501 |
502 | drm->drm_fd = open(kmsvnc->card, O_RDONLY);
503 | if (drm->drm_fd < 0)
504 | {
505 | KMSVNC_FATAL("card %s open failed: %s\n", kmsvnc->card, strerror(errno));
506 | }
507 | if (!kmsvnc->screen_blank && drmIsMaster(drm->drm_fd)) {
508 | if (drmDropMaster(drm->drm_fd)) fprintf(stderr, "Failed to drop master");
509 | }
510 | #ifndef DISABLE_KMSVNC_SCREEN_BLANK
511 | if (kmsvnc->screen_blank && !drmIsMaster(drm->drm_fd)) {
512 | drm->drm_master_fd = drm_get_master_fd();
513 | drm->drm_master_fd = drm->drm_master_fd > 0 ? drm->drm_master_fd : 0;
514 | if (kmsvnc->debug_enabled) {
515 | fprintf(stderr, "not master client, master fd %d\n", drm->drm_master_fd);
516 | }
517 | }
518 | #endif
519 |
520 | drm->drm_ver = drmGetVersion(drm->drm_fd);
521 | printf("drm driver is %s\n", drm->drm_ver->name);
522 |
523 | int err = drmSetClientCap(drm->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
524 | if (err < 0)
525 | {
526 | perror("Failed to set universal planes capability: primary planes will not be usable");
527 | }
528 |
529 | if (drm_refresh_planes(1)) return 1;
530 |
531 | #ifndef DISABLE_KMSVNC_SCREEN_BLANK
532 | if (kmsvnc->screen_blank) {
533 | drm->gamma = malloc(sizeof(struct kmsvnc_drm_gamma_data));
534 | if (!drm->gamma) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
535 | memset(drm->gamma, 0, sizeof(struct kmsvnc_drm_gamma_data));
536 | drmModeCrtc *target_crtc = drmModeGetCrtc(drm->drm_fd, drm->plane->crtc_id);
537 | if (target_crtc) {
538 | drm->gamma->size = (uint32_t)target_crtc->gamma_size;
539 | drm->gamma->red = malloc(drm->gamma->size*sizeof(uint16_t)*3);
540 | if (!drm->gamma->size) {
541 | fprintf(stderr, "drm->gamma->size = %u, not setting gamma.\n", drm->gamma->size);
542 | }
543 | else if (!drm->gamma->red) {
544 | fprintf(stderr, "memory allocation error at %s:%d\n", __FILE__, __LINE__);
545 | fprintf(stderr, "not setting gamma.\n");
546 | }
547 | else {
548 | memset(drm->gamma->red, 0, drm->gamma->size*sizeof(uint16_t)*3);
549 | drm->gamma->green = drm->gamma->red + drm->gamma->size;
550 | drm->gamma->blue = drm->gamma->red + drm->gamma->size*2;
551 | if (kmsvnc->screen_blank_restore) {
552 | int step = 0x10000 / drm->gamma->size;
553 | for (int i = 0; i < drm->gamma->size; i++) {
554 | drm->gamma->red[i] = drm->gamma->green[i] = drm->gamma->blue[i] = step * i;
555 | }
556 | }
557 | else {
558 | // legacy api, but weston also uses this, so whatever
559 | drmModeCrtcGetGamma(drm->drm_fd, drm->plane->crtc_id, drm->gamma->size, drm->gamma->red, drm->gamma->green, drm->gamma->blue);
560 | }
561 | if (kmsvnc->debug_enabled) {
562 | for (int i = 0; i < drm->gamma->size; i++) {
563 | fprintf(stderr, "gamma: %05d %05hu %05hu %05hu\n", i, drm->gamma->red[i], drm->gamma->green[i], drm->gamma->blue[i]);
564 | }
565 | }
566 | uint16_t *new_gamma_red = malloc(drm->gamma->size*sizeof(uint16_t)*3);
567 | if (!new_gamma_red) {
568 | fprintf(stderr, "memory allocation error at %s:%d\n", __FILE__, __LINE__);
569 | fprintf(stderr, "not setting gamma.\n");
570 | }
571 | else {
572 | memset(new_gamma_red, 0, drm->gamma->size*sizeof(uint16_t)*3);
573 | uint16_t *new_gamma_green = new_gamma_red + drm->gamma->size;
574 | uint16_t *new_gamma_blue = new_gamma_red + drm->gamma->size*2;
575 | if (drmModeCrtcSetGamma(drm->drm_master_fd ?: drm->drm_fd, drm->plane->crtc_id, drm->gamma->size, new_gamma_red, new_gamma_green, new_gamma_blue)) perror("Failed to set gamma");
576 | }
577 | if (new_gamma_red) {
578 | free(new_gamma_red);
579 | new_gamma_red = NULL;
580 | }
581 | }
582 | }
583 | else {
584 | fprintf(stderr, "Did not get a crtc structure, not setting gamma.\n");
585 | }
586 | if (target_crtc) {
587 | drmModeFreeCrtc(target_crtc);
588 | target_crtc = NULL;
589 | }
590 | }
591 | #endif
592 |
593 | drm->mfb = drmModeGetFB2(drm->drm_fd, drm->plane->fb_id);
594 | if (!drm->mfb) {
595 | KMSVNC_FATAL("Failed to get framebuffer %u: %s\n", drm->plane->fb_id, strerror(errno));
596 | }
597 | drm->pixfmt_name = drmGetFormatName(drm->mfb->pixel_format);
598 | drm->mod_vendor = drmGetFormatModifierVendor(drm->mfb->modifier);
599 | drm->mod_name = drmGetFormatModifierName(drm->mfb->modifier);
600 | printf("Template framebuffer is %u: %ux%u fourcc:%u mod:%lu flags:%u\n", drm->mfb->fb_id, drm->mfb->width, drm->mfb->height, drm->mfb->pixel_format, drm->mfb->modifier, drm->mfb->flags);
601 | printf("handles %u %u %u %u\n", drm->mfb->handles[0], drm->mfb->handles[1], drm->mfb->handles[2], drm->mfb->handles[3]);
602 | printf("offsets %u %u %u %u\n", drm->mfb->offsets[0], drm->mfb->offsets[1], drm->mfb->offsets[2], drm->mfb->offsets[3]);
603 | printf("pitches %u %u %u %u\n", drm->mfb->pitches[0], drm->mfb->pitches[1], drm->mfb->pitches[2], drm->mfb->pitches[3]);
604 | printf("format %s, modifier %s:%s\n", drm->pixfmt_name, drm->mod_vendor, drm->mod_name);
605 |
606 | if (!drm->mfb->handles[0])
607 | {
608 | KMSVNC_FATAL("No handle set on framebuffer: maybe you need some additional capabilities?\n");
609 | }
610 |
611 | drm->mmap_fd = drm->drm_fd;
612 | drm->mmap_size = drm->mfb->width * drm->mfb->height * BYTES_PER_PIXEL;
613 | drm->funcs = malloc(sizeof(struct kmsvnc_drm_funcs));
614 | if (!drm->funcs) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
615 | drm->funcs->convert = convert_bgra_to_rgba;
616 | drm->funcs->sync_start = drm_sync_noop;
617 | drm->funcs->sync_end = drm_sync_noop;
618 |
619 | if (drm_vendors()) return 1;
620 |
621 | return 0;
622 | }
623 |
624 |
625 | static int drm_kmsbuf_prime() {
626 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
627 |
628 | int err = drmPrimeHandleToFD(drm->drm_fd, drm->mfb->handles[0], O_RDWR, &drm->prime_fd);
629 | if (err < 0 || drm->prime_fd < 0)
630 | {
631 | KMSVNC_FATAL("Failed to get PRIME fd from framebuffer handle\n");
632 | }
633 | drm->funcs->sync_start = &drm_sync_start;
634 | drm->funcs->sync_end = &drm_sync_end;
635 | drm->mmap_fd = drm->prime_fd;
636 | return 0;
637 | }
638 |
639 | static int drm_kmsbuf_prime_vaapi() {
640 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
641 |
642 | int err = drmPrimeHandleToFD(drm->drm_fd, drm->mfb->handles[0], O_RDWR, &drm->prime_fd);
643 | if (err < 0 || drm->prime_fd < 0)
644 | {
645 | KMSVNC_FATAL("Failed to get PRIME fd from framebuffer handle\n");
646 | }
647 |
648 | if (va_init()) return 1;
649 |
650 | drm->mmap_fd = drm->prime_fd;
651 | drm->skip_map = 1;
652 | return 0;
653 | }
654 |
655 | static int drm_kmsbuf_dumb() {
656 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
657 |
658 | struct drm_gem_flink flink;
659 | flink.handle = drm->mfb->handles[0];
660 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_FLINK, &flink);
661 |
662 | struct drm_gem_open open_arg;
663 | open_arg.name = flink.name;
664 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
665 |
666 | struct drm_mode_map_dumb mreq;
667 | memset(&mreq, 0, sizeof(mreq));
668 | mreq.handle = open_arg.handle;
669 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
670 |
671 | drm->mmap_size = open_arg.size;
672 | drm->mmap_offset = mreq.offset;
673 | return 0;
674 | }
675 |
676 | int drm_vendors() {
677 | struct kmsvnc_drm_data *drm = kmsvnc->drm;
678 |
679 | char *driver_name;
680 | if (kmsvnc->force_driver) {
681 | printf("using %s instead of %s\n", kmsvnc->force_driver, drm->drm_ver->name);
682 | driver_name = kmsvnc->force_driver;
683 | }
684 | else {
685 | driver_name = drm->drm_ver->name;
686 | }
687 |
688 | if (strcmp(driver_name, "i915") == 0 || strcmp(driver_name, "amdgpu") == 0)
689 | {
690 | if (fourcc_mod_is_vendor(drm->mfb->modifier, INTEL)) {
691 | if (strstr(drm->mod_name, "CCS")) {
692 | printf("warn: intel with CCS modifier detected, please set INTEL_DEBUG=noccs\n");
693 | }
694 | };
695 | drm->funcs->convert = &convert_vaapi;
696 | if (drm_kmsbuf_prime_vaapi()) return 1;
697 | }
698 | else if (strcmp(driver_name, "nvidia-drm") == 0)
699 | {
700 | if (check_pixfmt_non_vaapi()) return 1;
701 | printf("warn: nvidia card detected. Currently only x-tiled framebuffer is supported. Performance may suffer.\n");
702 | if (drm->mfb->modifier != DRM_FORMAT_MOD_NONE && drm->mfb->modifier != DRM_FORMAT_MOD_LINEAR) {
703 | drm->funcs->convert = &convert_nvidia_x_tiled_kmsbuf;
704 | }
705 | if (drm_kmsbuf_dumb()) return 1;
706 | }
707 | else if (strcmp(driver_name, "vmwgfx") == 0 ||
708 | strcmp(driver_name, "vboxvideo") == 0 ||
709 | strcmp(driver_name, "virtio_gpu") == 0
710 | )
711 | {
712 | if (check_pixfmt_non_vaapi()) return 1;
713 | if (drm->mfb->modifier != DRM_FORMAT_MOD_NONE && drm->mfb->modifier != DRM_FORMAT_MOD_LINEAR) {
714 | printf("warn: modifier is not LINEAR, please create an issue with your modifier.\n");
715 | }
716 | // virgl does not work
717 | if (drm_kmsbuf_dumb()) return 1;
718 | }
719 | else if (strcmp(driver_name, "test-prime") == 0)
720 | {
721 | if (check_pixfmt_non_vaapi()) return 1;
722 | if (drm_kmsbuf_prime()) return 1;
723 | }
724 | else if (strcmp(driver_name, "test-map-dumb") == 0)
725 | {
726 | if (check_pixfmt_non_vaapi()) return 1;
727 | if (drm_kmsbuf_dumb()) return 1;
728 | }
729 | else if (strcmp(driver_name, "test-i915-gem") == 0)
730 | {
731 | if (check_pixfmt_non_vaapi()) return 1;
732 | struct drm_gem_flink flink;
733 | flink.handle = drm->mfb->handles[0];
734 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_FLINK, &flink);
735 |
736 | struct drm_gem_open open_arg;
737 | open_arg.name = flink.name;
738 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
739 |
740 | struct drm_i915_gem_mmap_gtt mmap_arg;
741 | mmap_arg.handle = open_arg.handle;
742 | DRM_IOCTL_MUST(drm->drm_fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg);
743 | drm->mmap_size = open_arg.size;
744 | drm->mmap_offset = mmap_arg.offset;
745 | }
746 | else if (strcmp(driver_name, "test-i915-prime-xtiled") == 0)
747 | {
748 | if (check_pixfmt_non_vaapi()) return 1;
749 | drm->funcs->convert = &convert_intel_x_tiled_kmsbuf;
750 | if (drm_kmsbuf_prime()) return 1;
751 | }
752 | else
753 | {
754 | if (check_pixfmt_non_vaapi()) return 1;
755 | fprintf(stderr, "Untested drm driver, use at your own risk!\n");
756 | if (drm->mfb->modifier != DRM_FORMAT_MOD_NONE && drm->mfb->modifier != DRM_FORMAT_MOD_LINEAR) {
757 | printf("warn: modifier is not LINEAR, please create an issue with your driver and modifier.\n");
758 | }
759 | if (drm_kmsbuf_dumb()) return 1;
760 | }
761 |
762 | if (!drm->skip_map && !drm->mapped)
763 | {
764 | printf("mapping with size = %lu, offset = %ld, fd = %d\n", drm->mmap_size, drm->mmap_offset, drm->mmap_fd);
765 | drm->mapped = mmap(NULL, drm->mmap_size, PROT_READ, MAP_SHARED, drm->mmap_fd, drm->mmap_offset);
766 | if (drm->mapped == MAP_FAILED)
767 | {
768 | KMSVNC_FATAL("Failed to mmap: %s\n", strerror(errno));
769 | }
770 | }
771 |
772 | return 0;
773 | }
774 |
--------------------------------------------------------------------------------
/drm.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "kmsvnc.h"
4 |
5 | #define DRM_IOCTL_MUST(...) do{ int e; if ((e = drmIoctl(__VA_ARGS__))) KMSVNC_FATAL("DRM ioctl error %d on line %d\n", e, __LINE__); } while(0)
6 | #define DRM_IOCTL_MAY(...) do{ int e; if ((e = drmIoctl(__VA_ARGS__))) fprintf(stderr, "DRM ioctl error %d on line %d\n", e, __LINE__); } while(0)
7 | #define DRM_R_IOCTL_MAY(...) do{ int e; if ((e = ioctl(__VA_ARGS__))) fprintf(stderr, "DRM ioctl error %d on line %d\n", e, __LINE__); } while(0)
8 |
9 |
10 | void drm_cleanup();
11 | int drm_open();
12 | int drm_vendors();
13 | int drm_dump_cursor_plane(char **data, int *width, int *height);
14 |
--------------------------------------------------------------------------------
/drm_master.c:
--------------------------------------------------------------------------------
1 | #define _GNU_SOURCE
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "drm_master.h"
12 |
13 | extern struct kmsvnc_data *kmsvnc;
14 |
15 |
16 | static inline int clone_fd(pid_t pid, int target_fd) {
17 | int pidfd = syscall(SYS_pidfd_open, pid, 0);
18 | if (pidfd <= 0) {
19 | perror("pidfd_open");
20 | return -1;
21 | }
22 | int cloned = syscall(SYS_pidfd_getfd, pidfd, target_fd, 0);
23 | if (cloned <= 0) {
24 | perror("pidfd_getfd");
25 | }
26 | close(pidfd);
27 | return cloned;
28 | }
29 |
30 | static inline int cmp_fds(pid_t pid, const char *drm_pth) {
31 | char path[PATH_MAX+1];
32 | snprintf(path, PATH_MAX+1, "/proc/%d/fd", pid);
33 |
34 | struct dirent **fdlist;
35 | int count = scandir(path, &fdlist, NULL, versionsort);
36 | int ret = -1;
37 | if (count >= 0) {
38 | for (int n = 0; n < count; n++) {
39 | if (ret == -1 && fdlist[n]->d_type == DT_LNK) {
40 | char link_pth[PATH_MAX+1];
41 | char real_pth[PATH_MAX+1];
42 | #pragma GCC diagnostic push
43 | #pragma GCC diagnostic ignored "-Wpragmas"
44 | #pragma GCC diagnostic ignored "-Wunknown-warning-option"
45 | #pragma GCC diagnostic ignored "-Wformat-truncation"
46 | snprintf(link_pth, PATH_MAX+1, "%s/%s", path, fdlist[n]->d_name);
47 | #pragma GCC diagnostic pop
48 | memset(real_pth, 0, PATH_MAX+1);
49 | #pragma GCC diagnostic push
50 | #pragma GCC diagnostic ignored "-Wunused-result"
51 | realpath(link_pth, real_pth);
52 | #pragma GCC diagnostic pop
53 | if (!strncmp(real_pth, drm_pth, PATH_MAX)) {
54 | int fd = atoi(fdlist[n]->d_name);
55 | if (fd > 0) {
56 | int cloned = clone_fd(pid, fd);
57 | if (cloned > 0 && drmIsMaster(cloned)) {
58 | ret = cloned;
59 | if (kmsvnc->debug_enabled) {
60 | fprintf(stderr, "found drm master pid=%d, fd=%d, cloned=%d\n", pid, fd, cloned);
61 | }
62 | }
63 | else {
64 | if (cloned > 0) close(cloned);
65 | }
66 | }
67 | }
68 | }
69 | free(fdlist[n]);
70 | fdlist[n] = NULL;
71 | }
72 | free(fdlist);
73 | fdlist = NULL;
74 | }
75 | return ret;
76 | }
77 |
78 | int drm_get_master_fd() {
79 | char drm_pth[PATH_MAX+1];
80 | memset(drm_pth, 0, PATH_MAX+1);
81 | #pragma GCC diagnostic push
82 | #pragma GCC diagnostic ignored "-Wunused-result"
83 | realpath(kmsvnc->card, drm_pth);
84 | #pragma GCC diagnostic pop
85 |
86 | struct dirent **proclist;
87 | int count = scandir("/proc", &proclist, NULL, versionsort);
88 | int ret = -1;
89 | if (count >= 0) {
90 | for (int n = 0; n < count; n++) {
91 | if (ret == -1 && proclist[n]->d_type == DT_DIR) {
92 | pid_t pid = (pid_t)atoi(proclist[n]->d_name);
93 | if (pid > 0) {
94 | int cloned = cmp_fds(pid, drm_pth);
95 | if (cloned > 0) {
96 | ret = cloned;
97 | }
98 | }
99 | }
100 | free(proclist[n]);
101 | proclist[n] = NULL;
102 | }
103 | free(proclist);
104 | proclist = NULL;
105 | }
106 | else {
107 | perror("open /proc");
108 | }
109 | return ret;
110 | }
111 |
--------------------------------------------------------------------------------
/drm_master.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "kmsvnc.h"
4 |
5 | int drm_get_master_fd();
6 |
--------------------------------------------------------------------------------
/input.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "input.h"
8 | #include "keymap.h"
9 |
10 | extern struct kmsvnc_data *kmsvnc;
11 |
12 | void uinput_cleanup()
13 | {
14 | if (kmsvnc->input) {
15 | if (kmsvnc->input->uinput_fd > 0){
16 | INP_IOCTL_MAY(kmsvnc->input->uinput_fd, UI_DEV_DESTROY);
17 | close(kmsvnc->input->uinput_fd);
18 | kmsvnc->input->uinput_fd = 0;
19 | }
20 | if (kmsvnc->input->keystate){
21 | free(kmsvnc->input->keystate);
22 | kmsvnc->input->keystate = NULL;
23 | }
24 | free(kmsvnc->input);
25 | kmsvnc->input = NULL;
26 | }
27 | }
28 |
29 | static void wake_system_up();
30 | int uinput_init()
31 | {
32 | struct kmsvnc_input_data *inp = malloc(sizeof(struct kmsvnc_input_data));
33 | if (!inp) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
34 | memset(inp, 0, sizeof(struct kmsvnc_input_data));
35 | kmsvnc->input = inp;
36 |
37 | inp->uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
38 | if (inp->uinput_fd <= 0)
39 | {
40 | KMSVNC_FATAL("Failed to open uinput\n");
41 | }
42 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_EVBIT, EV_KEY);
43 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_EVBIT, EV_SYN);
44 | for (int i = 0; i < UINPUT_MAX_KEY; i++)
45 | {
46 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_KEYBIT, i);
47 | }
48 |
49 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_EVBIT, EV_ABS);
50 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_ABSBIT, ABS_X);
51 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_ABSBIT, ABS_Y);
52 |
53 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_EVBIT, EV_REL);
54 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_RELBIT, REL_X);
55 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_RELBIT, REL_Y);
56 |
57 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_KEYBIT, BTN_LEFT);
58 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE);
59 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_KEYBIT, BTN_RIGHT);
60 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_EVBIT, EV_REL);
61 | INP_IOCTL_MUST(inp->uinput_fd, UI_SET_RELBIT, REL_WHEEL);
62 |
63 | struct uinput_abs_setup abs;
64 | memset(&abs, 0, sizeof(abs));
65 | abs.absinfo.maximum = UINPUT_ABS_MAX;
66 | abs.absinfo.minimum = 0;
67 | abs.code = ABS_X;
68 | INP_IOCTL_MUST(inp->uinput_fd, UI_ABS_SETUP, &abs);
69 | abs.code = ABS_Y;
70 | INP_IOCTL_MUST(inp->uinput_fd, UI_ABS_SETUP, &abs);
71 |
72 | struct uinput_setup usetup;
73 | memset(&usetup, 0, sizeof(usetup));
74 | usetup.id.bustype = BUS_USB;
75 | usetup.id.vendor = 0x0011;
76 | usetup.id.product = 0x4514;
77 | strcpy(usetup.name, "kmsvnc");
78 |
79 | INP_IOCTL_MUST(inp->uinput_fd, UI_DEV_SETUP, &usetup);
80 | INP_IOCTL_MUST(inp->uinput_fd, UI_DEV_CREATE);
81 |
82 | inp->keystate = malloc(UINPUT_MAX_KEY);
83 | if (!inp->keystate) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
84 | memset(inp->keystate, 0, UINPUT_MAX_KEY);
85 |
86 | if (kmsvnc->input_wakeup) {
87 | printf("waiting for 1 second for userspace to detect the input devive...\n");
88 | sleep(1);
89 | wake_system_up();
90 | printf("waiting for 1 second for mouse input to be processed...\n");
91 | sleep(1);
92 | }
93 | return 0;
94 | }
95 |
96 | void rfb_key_hook(rfbBool down, rfbKeySym keysym, rfbClientPtr cl)
97 | {
98 | struct key_iter_search search = {
99 | .keysym = keysym,
100 | .keycode = XKB_KEYCODE_INVALID,
101 | .level = 0,
102 | };
103 | xkb_keymap_key_for_each(kmsvnc->keymap->map, key_iter, &search);
104 | if (search.keycode == XKB_KEYCODE_INVALID)
105 | {
106 | fprintf(stderr, "Keysym %04x not found in our keymap\n", keysym);
107 | return;
108 | }
109 | // printf("key %s, keysym %04x, keycode %u\n", down ? "down" : "up", keysym, search.keycode);
110 | if (search.keycode >= UINPUT_MAX_KEY)
111 | {
112 | fprintf(stderr, "Keycode %d >= %d\n", search.keycode, UINPUT_MAX_KEY);
113 | return;
114 | }
115 | if (down != kmsvnc->input->keystate[search.keycode])
116 | {
117 | struct input_event ies[] = {
118 | {
119 | .type = EV_KEY,
120 | .code = search.keycode - 8, // magic
121 | .value = down,
122 | .time.tv_sec = 0,
123 | .time.tv_usec = 0,
124 | },
125 | {
126 | .type = EV_SYN,
127 | .code = SYN_REPORT,
128 | .value = 0,
129 | },
130 | };
131 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(ies); i++)
132 | {
133 | KMSVNC_WRITE_MAY(kmsvnc->input->uinput_fd, &ies[i], sizeof(ies[0]));
134 | }
135 |
136 | kmsvnc->input->keystate[search.keycode] = down;
137 | }
138 | }
139 |
140 | void rfb_ptr_hook(int mask, int screen_x, int screen_y, rfbClientPtr cl)
141 | {
142 | // printf("pointer to %d, %d\n", screen_x, screen_y);
143 | float global_x = (float)(screen_x + kmsvnc->input_offx);
144 | float global_y = (float)(screen_y + kmsvnc->input_offy);
145 | int touch_x = round(global_x / (kmsvnc->input_width ?: kmsvnc->drm->mfb->width) * UINPUT_ABS_MAX);
146 | int touch_y = round(global_y / (kmsvnc->input_height ?: kmsvnc->drm->mfb->height) * UINPUT_ABS_MAX);
147 | struct input_event ies1[] = {
148 | {
149 | .type = EV_ABS,
150 | .code = ABS_X,
151 | .value = touch_x,
152 | },
153 | {
154 | .type = EV_ABS,
155 | .code = ABS_Y,
156 | .value = touch_y,
157 | },
158 | {
159 | .type = EV_KEY,
160 | .code = BTN_LEFT,
161 | .value = !!(mask & 0b1)},
162 | {
163 | .type = EV_KEY,
164 | .code = BTN_MIDDLE,
165 | .value = !!(mask & 0b10)},
166 | {
167 | .type = EV_KEY,
168 | .code = BTN_RIGHT,
169 | .value = !!(mask & 0b100)},
170 | {
171 | .type = EV_SYN,
172 | .code = SYN_REPORT,
173 | .value = 0,
174 | },
175 | };
176 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(ies1); i++)
177 | {
178 | KMSVNC_WRITE_MAY(kmsvnc->input->uinput_fd, &ies1[i], sizeof(ies1[0]));
179 | }
180 | if (mask & 0b11000)
181 | {
182 | struct input_event ies2[] = {
183 | {
184 | .type = EV_REL,
185 | .code = REL_WHEEL,
186 | .value = mask & 0b1000 ? 1 : -1,
187 | },
188 | {
189 | .type = EV_SYN,
190 | .code = SYN_REPORT,
191 | .value = 0,
192 | },
193 | };
194 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(ies2); i++)
195 | {
196 | KMSVNC_WRITE_MAY(kmsvnc->input->uinput_fd, &ies2[i], sizeof(ies2[0]));
197 | }
198 | }
199 | }
200 |
201 | static void wake_system_up()
202 | {
203 | struct input_event ies1[] = {
204 | {
205 | .type = EV_REL,
206 | .code = REL_X,
207 | .value = 1,
208 | },
209 | {
210 | .type = EV_SYN,
211 | .code = SYN_REPORT,
212 | .value = 0,
213 | },
214 | {
215 | .type = EV_REL,
216 | .code = REL_X,
217 | .value = -1,
218 | },
219 | {
220 | .type = EV_SYN,
221 | .code = SYN_REPORT,
222 | .value = 0,
223 | },
224 | };
225 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(ies1); i++)
226 | {
227 | KMSVNC_WRITE_MAY(kmsvnc->input->uinput_fd, &ies1[i], sizeof(ies1[0]));
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/input.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "kmsvnc.h"
6 |
7 | #define UINPUT_ABS_MAX INT16_MAX
8 | #define UINPUT_MAX_KEY 256
9 |
10 | #define INP_IOCTL_MUST(...) do{ int e; if ((e = ioctl(__VA_ARGS__))) KMSVNC_FATAL("uinput ioctl error %d on line %d\n", e, __LINE__); } while(0)
11 | #define INP_IOCTL_MAY(...) do{ int e; if ((e = ioctl(__VA_ARGS__))) fprintf(stderr, "uinput ioctl error %d on line %d\n", e, __LINE__); } while(0)
12 |
13 | void uinput_cleanup();
14 | int uinput_init();
15 | void rfb_key_hook(rfbBool down, rfbKeySym keysym, rfbClientPtr cl);
16 | void rfb_ptr_hook(int mask, int screen_x, int screen_y, rfbClientPtr cl);
17 |
--------------------------------------------------------------------------------
/keymap.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "keymap.h"
7 |
8 | extern struct kmsvnc_data *kmsvnc;
9 |
10 | void xkb_cleanup() {
11 | if (kmsvnc->keymap) {
12 | if (kmsvnc->keymap->map) {
13 | xkb_keymap_unref(kmsvnc->keymap->map);
14 | kmsvnc->keymap->map = NULL;
15 | }
16 | if (kmsvnc->keymap->ctx) {
17 | xkb_context_unref(kmsvnc->keymap->ctx);
18 | kmsvnc->keymap->ctx = NULL;
19 | }
20 | free(kmsvnc->keymap);
21 | kmsvnc->keymap = NULL;
22 | }
23 | }
24 |
25 | int xkb_init()
26 | {
27 | struct kmsvnc_keymap_data *xkb = malloc(sizeof(struct kmsvnc_keymap_data));
28 | if (!xkb) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
29 | memset(xkb, 0, sizeof(struct kmsvnc_keymap_data));
30 | kmsvnc->keymap = xkb;
31 |
32 | xkb->ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
33 | if (xkb->ctx == NULL)
34 | {
35 | KMSVNC_FATAL("Failed to create XKB context\n");
36 | }
37 | struct xkb_rule_names names = {
38 | .rules = NULL,
39 | .model = NULL,
40 | .layout = NULL,
41 | .variant = NULL,
42 | .options = NULL,
43 | };
44 | xkb->map = xkb_keymap_new_from_names(xkb->ctx, &names, 0);
45 | if (xkb->map == NULL)
46 | {
47 | KMSVNC_FATAL("Failed to create XKB keymap\n");
48 | }
49 | // printf("xkb: keymap string\n%s\n", xkb_keymap_get_as_string(xkb->map, XKB_KEYMAP_USE_ORIGINAL_FORMAT));
50 | return 0;
51 | }
52 |
53 |
54 | void key_iter(struct xkb_keymap *xkb, xkb_keycode_t key, void *data)
55 | {
56 | struct key_iter_search *search = data;
57 | if (search->keycode != XKB_KEYCODE_INVALID)
58 | {
59 | return; // We are done
60 | }
61 | xkb_level_index_t num_levels = xkb_keymap_num_levels_for_key(xkb, key, 0);
62 | for (xkb_level_index_t i = 0; i < num_levels; i++)
63 | {
64 | const xkb_keysym_t *syms;
65 | int num_syms = xkb_keymap_key_get_syms_by_level(xkb, key, 0, i, &syms);
66 | for (int k = 0; k < num_syms; k++)
67 | {
68 | if (syms[k] == search->keysym)
69 | {
70 | search->keycode = key;
71 | search->level = i;
72 | goto end;
73 | }
74 | }
75 | }
76 | end:
77 | return;
78 | }
79 |
--------------------------------------------------------------------------------
/keymap.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "kmsvnc.h"
4 |
5 | void xkb_cleanup();
6 | int xkb_init();
7 | void key_iter(struct xkb_keymap *xkb, xkb_keycode_t key, void *data);
8 |
--------------------------------------------------------------------------------
/kmsvnc.c:
--------------------------------------------------------------------------------
1 | #define _GNU_SOURCE
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "kmsvnc.h"
14 | #include "keymap.h"
15 | #include "input.h"
16 | #include "drm.h"
17 | #include "va.h"
18 |
19 | struct kmsvnc_data *kmsvnc = NULL;
20 |
21 | #define NS_IN_S 1000000000
22 |
23 | static void between_frames()
24 | {
25 | static struct timespec now = {0, 0}, then = {0, 0}, tmp = {0, 0};
26 |
27 | clock_gettime(CLOCK_MONOTONIC, &now);
28 | memcpy((char *)&then, (char *)&tmp, sizeof(struct timespec));
29 | tmp.tv_nsec += kmsvnc->vnc_opt->sleep_ns;
30 | if (tmp.tv_nsec >= NS_IN_S)
31 | {
32 | tmp.tv_sec++;
33 | tmp.tv_nsec %= NS_IN_S;
34 | }
35 | if (now.tv_sec < tmp.tv_sec || (now.tv_sec == tmp.tv_sec && now.tv_nsec < tmp.tv_nsec))
36 | {
37 | then.tv_sec = tmp.tv_sec - now.tv_sec;
38 | then.tv_nsec = tmp.tv_nsec - now.tv_nsec;
39 | if (then.tv_nsec < 0)
40 | {
41 | then.tv_sec--;
42 | then.tv_nsec += NS_IN_S;
43 | }
44 | nanosleep(&then, &then);
45 | }
46 | memcpy((char *)&now, (char *)&then, sizeof(struct timespec));
47 | }
48 |
49 | static void update_screen_buf(char* to, char *from, int width, int height) {
50 | uint64_t *double_pix_from = (uint64_t *)from;
51 | uint64_t *double_pix_to = (uint64_t *)to;
52 | int min_x = INT32_MAX;
53 | int min_y = INT32_MAX;
54 | int max_x = -1;
55 | int max_y = -1;
56 | if (!kmsvnc->vnc_opt->disable_cmpfb && width % 2 == 0) {
57 | for (int y = 0; y < height; y++) {
58 | for (int x = 0; x < width; x+=2) {
59 | if (*double_pix_from != *double_pix_to) {
60 | if (x < min_x) {
61 | min_x = x;
62 | }
63 | if (x > max_x) {
64 | max_x = x;
65 | }
66 | if (y < min_y) {
67 | min_y = y;
68 | }
69 | if (y > max_y) {
70 | max_y = y;
71 | }
72 | }
73 | double_pix_from ++;
74 | double_pix_to ++;
75 | }
76 | }
77 | }
78 | else {
79 | memcpy(to, from, width * height * BYTES_PER_PIXEL);
80 | rfbMarkRectAsModified(kmsvnc->server, 0, 0, width, height);
81 | return;
82 | }
83 | max_x = max_x < 0 ? 0 : max_x;
84 | max_y = max_y < 0 ? 0 : max_y;
85 | min_x = min_x > width ? 0 : min_x;
86 | min_y = min_y > height ? 0 : min_y;
87 |
88 | //printf("dirty: %d, %d, %d, %d\n", min_x, min_y, max_x, max_y);
89 | if (max_x || max_y || min_x || min_y) {
90 | memcpy(to, from, width * height * BYTES_PER_PIXEL);
91 | rfbMarkRectAsModified(kmsvnc->server, min_x, min_y, max_x + 2, max_y + 1);
92 | }
93 | }
94 |
95 | static inline void update_vnc_cursor(char *data, int width, int height) {
96 | uint8_t r, g, b, a;
97 | #define CURSOR_MIN_A 160 // ~63%
98 | int min_x = width;
99 | int max_x = -1;
100 | int min_y = height;
101 | int max_y = -1;
102 | int x, y;
103 |
104 | for (int i = 0; i < width * height * BYTES_PER_PIXEL; i += BYTES_PER_PIXEL) {
105 | uint32_t pixdata = htonl(*((uint32_t*)(data + i)));
106 | //r = (pixdata & 0xff000000u) >> 24;
107 | //g = (pixdata & 0x00ff0000u) >> 16;
108 | //b = (pixdata & 0x0000ff00u) >> 8;
109 | a = pixdata & 0xff;
110 | if (a > CURSOR_MIN_A) {
111 | x = (i / BYTES_PER_PIXEL) % width;
112 | y = (i / BYTES_PER_PIXEL) / width;
113 | if (x < min_x) min_x = x;
114 | if (y < min_y) min_y = y;
115 | if (x > max_x) max_x = x;
116 | if (y > max_y) max_y = y;
117 | }
118 | }
119 | if (min_x > max_x || min_y > max_y) {
120 | // no cursor detected
121 | return;
122 | }
123 | int rwidth = max_x - min_x + 1;
124 | int rheight = max_y - min_y + 1;
125 | if (kmsvnc->cursor_bitmap_len < rwidth * rheight * BYTES_PER_PIXEL)
126 | {
127 | if (kmsvnc->cursor_bitmap)
128 | free(kmsvnc->cursor_bitmap);
129 | kmsvnc->cursor_bitmap = malloc(rwidth * rheight * BYTES_PER_PIXEL);
130 | if (!kmsvnc->cursor_bitmap) return;
131 | kmsvnc->cursor_bitmap_len = rwidth * rheight * BYTES_PER_PIXEL;
132 | }
133 | unsigned char *rich_source = malloc(rwidth * rheight * BYTES_PER_PIXEL);
134 | if (!rich_source) return;
135 | char *maskString = malloc(rwidth * rheight);
136 | if (!maskString) {
137 | free(rich_source);
138 | return;
139 | }
140 | memset(maskString, ' ', rwidth * rheight);
141 | for (int i = 0; i < rwidth; i++) {
142 | for (int j = 0; j < rheight; j++) {
143 | int t = (i + j * rwidth) * BYTES_PER_PIXEL;
144 | int s = ((i+min_x) + (j+min_y) * width) * BYTES_PER_PIXEL;
145 | *((uint32_t*)(rich_source + t)) = *((uint32_t*)(data + s));
146 | if ((uint8_t)*(rich_source + t + 3) > CURSOR_MIN_A) {
147 | maskString[i + j * rwidth] = 'x';
148 | }
149 | }
150 | }
151 |
152 | if ((kmsvnc->server->cursor->width != rwidth || kmsvnc->server->cursor->height != rheight) || memcmp(kmsvnc->cursor_bitmap, rich_source, rwidth * rheight * BYTES_PER_PIXEL)) {
153 | KMSVNC_DEBUG("cursor update %dx%d\n", rwidth, rheight);
154 | memcpy(kmsvnc->cursor_bitmap, rich_source, kmsvnc->cursor_bitmap_len);
155 | char *cursorString = malloc(rwidth * rheight);
156 | if (!cursorString) {
157 | free(rich_source);
158 | free(maskString);
159 | return;
160 | }
161 |
162 | memset(cursorString, 'x', rwidth * rheight);
163 |
164 | rfbCursorPtr cursor = rfbMakeXCursor(rwidth, rheight, cursorString, maskString);
165 | free(cursorString);
166 | cursor->richSource = rich_source;
167 | cursor->cleanupRichSource = TRUE;
168 | cursor->xhot = 0;
169 | cursor->yhot = 0;
170 | rfbSetCursor(kmsvnc->server, cursor);
171 | }
172 | else {
173 | free(rich_source);
174 | free(maskString);
175 | }
176 | }
177 |
178 | static void cleanup() {
179 | if (kmsvnc->keymap) {
180 | xkb_cleanup();
181 | }
182 | if (kmsvnc->input) {
183 | uinput_cleanup();
184 | }
185 | if (kmsvnc->drm) {
186 | drm_cleanup();
187 | }
188 | if (kmsvnc->va) {
189 | va_cleanup();
190 | }
191 | if (kmsvnc) {
192 | if (kmsvnc->vnc_opt) {
193 | free(kmsvnc->vnc_opt);
194 | kmsvnc->vnc_opt = NULL;
195 | }
196 | if (kmsvnc->buf1) {
197 | free(kmsvnc->buf1);
198 | kmsvnc->buf1 = NULL;
199 | }
200 | if (kmsvnc->buf) {
201 | free(kmsvnc->buf);
202 | kmsvnc->buf = NULL;
203 | }
204 | if (kmsvnc->cursor_bitmap) {
205 | free(kmsvnc->cursor_bitmap);
206 | kmsvnc->cursor_bitmap = NULL;
207 | }
208 | kmsvnc->cursor_bitmap_len = 0;
209 | free(kmsvnc);
210 | kmsvnc = NULL;
211 | }
212 | }
213 |
214 | void signal_handler_noop(int signum){}
215 | void signal_handler(int signum){
216 | if (kmsvnc->shutdown) {
217 | return;
218 | }
219 | kmsvnc->shutdown = 1;
220 | if (kmsvnc->server) {
221 | rfbShutdownServer(kmsvnc->server,TRUE);
222 | }
223 | }
224 |
225 | static struct argp_option kmsvnc_main_options[] = {
226 | {"device", 'd', "/dev/dri/cardX", 0, "DRM device"},
227 | {"source-plane", 0xfefc, "0", 0, "Use specific plane"},
228 | {"source-crtc", 0xfefd, "0", 0, "Use specific crtc (to list all crtcs and planes, set this to -1)"},
229 | {"force-driver", 0xfefe, "i915", 0, "force a certain driver (for debugging)"},
230 | {"bind", 'b', "0.0.0.0", 0, "Listen on (ipv4 address)"},
231 | {"bind6", 0xfeff, "::", 0, "Listen on (ipv6 address)"},
232 | {"port", 'p', "5900", 0, "Listen port"},
233 | {"disable-ipv6", '4', 0, OPTION_ARG_OPTIONAL, "Disable ipv6"},
234 | {"fps", 0xff00, "30", 0, "Target frames per second"},
235 | {"disable-always-shared", 0xff01, 0, OPTION_ARG_OPTIONAL, "Do not always treat incoming connections as shared"},
236 | {"disable-compare-fb", 0xff02, 0, OPTION_ARG_OPTIONAL, "Do not compare pixels"},
237 | {"capture-cursor", 'c', 0, OPTION_ARG_OPTIONAL, "Capture mouse cursor"},
238 | {"capture-raw-fb", 0xff03, "/tmp/rawfb.bin", 0, "Capture RAW framebuffer instead of starting the vnc server (for debugging)"},
239 | {"va-derive", 0xff04, "off", 0, "Enable derive with vaapi"},
240 | {"debug", 0xff05, 0, OPTION_ARG_OPTIONAL, "Print debug message"},
241 | {"input-width", 0xff06, "0", 0, "Explicitly set input width, normally this is inferred from screen width on a single display system"},
242 | {"input-height", 0xff07, "0", 0, "Explicitly set input height"},
243 | {"input-offx", 0xff08, "0", 0, "Set input offset of x axis on a multi display system"},
244 | {"input-offy", 0xff09, "0", 0, "Set input offset of y axis on a multi display system"},
245 | #ifndef DISABLE_KMSVNC_SCREEN_BLANK
246 | {"screen-blank", 0xff0a, 0, OPTION_ARG_OPTIONAL, "Blank screen with gamma set on crtc"},
247 | {"screen-blank-restore-linear", 0xff0b, 0, OPTION_ARG_OPTIONAL, "Restore linear values on exit in case of messed up gamma"},
248 | #endif
249 | {"va-byteorder-swap", 0xff0c, 0, OPTION_ARG_OPTIONAL, "Force swap vaapi image rgb byteorder"},
250 | {"wakeup", 'w', 0, OPTION_ARG_OPTIONAL, "Move mouse to wake the system up before start"},
251 | {"disable-input", 'i', 0, OPTION_ARG_OPTIONAL, "Disable uinput"},
252 | {"desktop-name", 'n', "kmsvnc", 0, "Specify vnc desktop name"},
253 | {"password-file", 0xff0d, "", 0, "File containing password (max 8 characters)"},
254 | {0}
255 | };
256 |
257 | static error_t parse_opt(int key, char *arg, struct argp_state *state) {
258 | int *arg_cout = state->input;
259 |
260 | switch (key) {
261 | case 'd':
262 | kmsvnc->card = arg;
263 | break;
264 | case 0xfefc:
265 | kmsvnc->source_plane = atoi(arg);
266 | break;
267 | case 0xfefd:
268 | kmsvnc->source_crtc = atoi(arg);
269 | break;
270 | case 0xfefe:
271 | kmsvnc->force_driver = arg;
272 | break;
273 | case 'b':
274 | if (!inet_aton(arg, kmsvnc->vnc_opt->bind)) {
275 | argp_error(state, "invalid ipv4 address %s", arg);
276 | }
277 | break;
278 | case 0xfeff:
279 | kmsvnc->vnc_opt->bind6 = arg;
280 | break;
281 | case 'p':
282 | {
283 | int port = atoi(arg);
284 | if (port > 0 && port < 65536) {
285 | kmsvnc->vnc_opt->port = port;
286 | }
287 | else {
288 | argp_error(state, "invalid port %s", arg);
289 | }
290 | }
291 | break;
292 | case '4':
293 | kmsvnc->vnc_opt->disable_ipv6 = 1;
294 | break;
295 | case 0xff00:
296 | {
297 | int fps = atoi(arg);
298 | if (fps > 0 && fps < 1000) {
299 | kmsvnc->vnc_opt->sleep_ns = NS_IN_S / fps;
300 | }
301 | else {
302 | argp_error(state, "invalid fps %s", arg);
303 | }
304 | }
305 | break;
306 | case 0xff01:
307 | kmsvnc->vnc_opt->always_shared = 0;
308 | break;
309 | case 0xff02:
310 | kmsvnc->vnc_opt->disable_cmpfb = 1;
311 | break;
312 | case 'c':
313 | kmsvnc->capture_cursor = 1;
314 | break;
315 | case 0xff03:
316 | kmsvnc->debug_capture_fb = arg;
317 | kmsvnc->disable_input = 1;
318 | break;
319 | case 0xff04:
320 | if (!strcmp("on", arg) || !strcmp("y", arg) || !strcmp("yes", arg) || !strcmp("1", arg)) {
321 | kmsvnc->va_derive_enabled = 1;
322 | }
323 | else {
324 | kmsvnc->va_derive_enabled = 0;
325 | }
326 | break;
327 | case 0xff05:
328 | kmsvnc->debug_enabled = 1;
329 | break;
330 | case 0xff06:
331 | {
332 | int width = atoi(arg);
333 | if (width > 0) {
334 | kmsvnc->input_width = width;
335 | }
336 | }
337 | break;
338 | case 0xff07:
339 | {
340 | int height = atoi(arg);
341 | if (height > 0) {
342 | kmsvnc->input_height = height;
343 | }
344 | }
345 | break;
346 | case 0xff08:
347 | {
348 | int offset_x = atoi(arg);
349 | if (offset_x > 0) {
350 | kmsvnc->input_offx = offset_x;
351 | }
352 | }
353 | break;
354 | case 0xff09:
355 | {
356 | int offset_y = atoi(arg);
357 | if (offset_y > 0) {
358 | kmsvnc->input_offy = offset_y;
359 | }
360 | }
361 | break;
362 | case 0xff0a:
363 | kmsvnc->screen_blank = 1;
364 | break;
365 | case 0xff0b:
366 | kmsvnc->screen_blank_restore = 1;
367 | break;
368 | case 0xff0c:
369 | kmsvnc->va_byteorder_swap = 1;
370 | break;
371 | case 0xff0d:
372 | kmsvnc->vnc_opt->password_file = arg;
373 | break;
374 | case 'w':
375 | kmsvnc->input_wakeup = 1;
376 | break;
377 | case 'i':
378 | kmsvnc->disable_input = 1;
379 | break;
380 | case 'n':
381 | kmsvnc->vnc_opt->desktop_name = arg;
382 | break;
383 | case ARGP_KEY_ARG:
384 | return ARGP_ERR_UNKNOWN;
385 | default:
386 | return ARGP_ERR_UNKNOWN;
387 | }
388 | return 0;
389 | }
390 |
391 | int main(int argc, char **argv)
392 | {
393 | kmsvnc = malloc(sizeof(struct kmsvnc_data));
394 | if (!kmsvnc) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
395 | memset(kmsvnc, 0, sizeof(struct kmsvnc_data));
396 |
397 | struct vnc_opt *vncopt = malloc(sizeof(struct vnc_opt));
398 | if (!vncopt) {
399 | free(kmsvnc);
400 | KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
401 | }
402 | memset(vncopt, 0, sizeof(struct vnc_opt));
403 |
404 | kmsvnc->vnc_opt = vncopt;
405 |
406 | #define DEVICE_EXAMPLE_MAX_SIZE 15
407 | #define DEVICE_EXAMPLE_FALLBACK "/dev/dri/card0"
408 | static char device_example[DEVICE_EXAMPLE_MAX_SIZE] = DEVICE_EXAMPLE_FALLBACK;
409 | kmsvnc->card = device_example;
410 | kmsvnc->va_derive_enabled = -1;
411 | kmsvnc->vnc_opt->bind = &(struct in_addr){0};
412 | kmsvnc->vnc_opt->always_shared = 1;
413 | kmsvnc->vnc_opt->port = 5900;
414 | kmsvnc->vnc_opt->sleep_ns = NS_IN_S / 30;
415 | kmsvnc->vnc_opt->desktop_name = "kmsvnc";
416 |
417 | static char *args_doc = "";
418 | static char *doc = "kmsvnc -- vncserver for DRM/KMS capable GNU/Linux devices";
419 |
420 | struct argp argp = {kmsvnc_main_options, parse_opt, args_doc, doc};
421 | argp_parse(&argp, argc, argv, 0, 0, NULL);
422 |
423 | if (kmsvnc->card == device_example) {
424 | for (int i = 0; i < 10; i++) {
425 | snprintf(kmsvnc->card, DEVICE_EXAMPLE_MAX_SIZE, "/dev/dri/card%d", i);
426 | if (!access(kmsvnc->card, F_OK)) {
427 | break;
428 | }
429 | else {
430 | snprintf(kmsvnc->card, DEVICE_EXAMPLE_MAX_SIZE, DEVICE_EXAMPLE_FALLBACK);
431 | }
432 | }
433 | }
434 |
435 | if (!kmsvnc->disable_input) {
436 | const char* XKB_DEFAULT_LAYOUT = getenv("XKB_DEFAULT_LAYOUT");
437 | if (!XKB_DEFAULT_LAYOUT || strcmp(XKB_DEFAULT_LAYOUT, "") == 0) {
438 | printf("No keyboard layout set from environment variables, use US layout by default\n");
439 | printf("See https://xkbcommon.org/doc/current/structxkb__rule__names.html\n");
440 | setenv("XKB_DEFAULT_LAYOUT", "us", 1);
441 | }
442 |
443 | if (xkb_init()) {
444 | cleanup();
445 | return 1;
446 | }
447 | if (uinput_init()) {
448 | cleanup();
449 | return 1;
450 | }
451 | }
452 | if (drm_open()) {
453 | cleanup();
454 | return 1;
455 | }
456 |
457 | if (kmsvnc->debug_capture_fb) {
458 | int wfd = open(kmsvnc->debug_capture_fb, O_WRONLY | O_CREAT, 00644);
459 | int max_size = 0;
460 | for (int i = 0; i < 4; i++) {
461 | int size = kmsvnc->drm->mfb->offsets[i] + kmsvnc->drm->mfb->height * kmsvnc->drm->mfb->pitches[i];
462 | if (size > max_size) max_size = size;
463 | }
464 | printf("attempt to write %d bytes\n", max_size);
465 | if (wfd > 0) {
466 | if (kmsvnc->va) {
467 | if (!kmsvnc->drm->mapped) kmsvnc->drm->mapped = malloc(max_size);
468 | if (!kmsvnc->drm->mapped) {
469 | cleanup();
470 | KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
471 | }
472 | va_hwframe_to_vaapi(kmsvnc->drm->mapped);
473 | }
474 | KMSVNC_WRITE_MAY(wfd, kmsvnc->drm->mapped, (ssize_t)max_size);
475 | fsync(wfd);
476 | printf("wrote raw frame buffer to %s\n", kmsvnc->debug_capture_fb);
477 | }
478 | else {
479 | fprintf(stderr, "open file %s failed, %s\n", kmsvnc->debug_capture_fb, strerror(errno));
480 | }
481 | if (kmsvnc->screen_blank) {
482 | sigset_t signal_set;
483 | int sig;
484 | sigemptyset(&signal_set);
485 | signal(SIGHUP, &signal_handler_noop);
486 | signal(SIGINT, &signal_handler_noop);
487 | signal(SIGTERM, &signal_handler_noop);
488 | sigaddset(&signal_set, SIGHUP);
489 | sigaddset(&signal_set, SIGINT);
490 | sigaddset(&signal_set, SIGTERM);
491 | fprintf(stderr, "blanking screen...\n");
492 | sigwait(&signal_set, &sig);
493 | fprintf(stderr, "got sig %d\n", sig);
494 | }
495 | cleanup();
496 | return 0;
497 | }
498 |
499 | size_t buflen = kmsvnc->drm->mfb->width * kmsvnc->drm->mfb->height * BYTES_PER_PIXEL;
500 | kmsvnc->buf = malloc(buflen);
501 | if (!kmsvnc->buf) {
502 | cleanup();
503 | KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
504 | }
505 | memset(kmsvnc->buf, 0, buflen);
506 | kmsvnc->buf1 = malloc(buflen);
507 | if (!kmsvnc->buf1) {
508 | cleanup();
509 | KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
510 | }
511 | memset(kmsvnc->buf1, 0, buflen);
512 |
513 | signal(SIGHUP, &signal_handler);
514 | signal(SIGINT, &signal_handler);
515 | signal(SIGTERM, &signal_handler);
516 |
517 | kmsvnc->server = rfbGetScreen(0, NULL, kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, 8, 3, 4);
518 | if (!kmsvnc->server) {
519 | cleanup();
520 | return 1;
521 | }
522 | kmsvnc->server->desktopName = kmsvnc->vnc_opt->desktop_name;
523 | kmsvnc->server->frameBuffer = kmsvnc->buf;
524 | kmsvnc->server->port = kmsvnc->vnc_opt->port;
525 | kmsvnc->server->listenInterface = kmsvnc->vnc_opt->bind->s_addr;
526 | kmsvnc->server->ipv6port = kmsvnc->vnc_opt->disable_ipv6 ? 0 : kmsvnc->vnc_opt->port;
527 | kmsvnc->server->listen6Interface = kmsvnc->vnc_opt->bind6;
528 | kmsvnc->server->alwaysShared = kmsvnc->vnc_opt->always_shared;
529 | if (!kmsvnc->disable_input) {
530 | kmsvnc->server->kbdAddEvent = rfb_key_hook;
531 | kmsvnc->server->ptrAddEvent = rfb_ptr_hook;
532 | }
533 | if (kmsvnc->vnc_opt->password_file) {
534 | static char password[9] = "";
535 | static const char* passwords[2] = { password, 0 };
536 | FILE *password_file = fopen(kmsvnc->vnc_opt->password_file, "r");
537 |
538 | if (password_file) {
539 | fgets(password, sizeof(password), password_file);
540 | fclose(password_file);
541 | }
542 |
543 | if (*password) password[strcspn(password, "\n")] = '\0';
544 |
545 | if (*password) {
546 | kmsvnc->server->authPasswdData = passwords;
547 | kmsvnc->server->passwordCheck = rfbCheckPasswordByList;
548 | }
549 | }
550 | rfbInitServer(kmsvnc->server);
551 | rfbRunEventLoop(kmsvnc->server, -1, TRUE);
552 | int cursor_frame = 0;
553 | while (rfbIsActive(kmsvnc->server))
554 | {
555 | between_frames();
556 | if (kmsvnc->server->clientHead)
557 | {
558 | kmsvnc->drm->funcs->sync_start(kmsvnc->drm->prime_fd);
559 | kmsvnc->drm->funcs->convert(kmsvnc->drm->mapped, kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, kmsvnc->buf1);
560 | kmsvnc->drm->funcs->sync_end(kmsvnc->drm->prime_fd);
561 | update_screen_buf(kmsvnc->buf, kmsvnc->buf1, kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height);
562 | if (kmsvnc->capture_cursor) {
563 | cursor_frame++;
564 | cursor_frame %= CURSOR_FRAMESKIP;
565 | if (!cursor_frame) {
566 | char *data = NULL;
567 | int width, height;
568 | int err = drm_dump_cursor_plane(&data, &width, &height);
569 | if (!err && data) {
570 | update_vnc_cursor(data, width, height);
571 | }
572 | }
573 | }
574 | }
575 | }
576 | cleanup();
577 | return 0;
578 | }
579 |
--------------------------------------------------------------------------------
/kmsvnc.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 |
15 | #define BYTES_PER_PIXEL 4
16 | #define CURSOR_FRAMESKIP 15
17 |
18 | struct vnc_opt
19 | {
20 | int port;
21 | struct in_addr *bind;
22 | char *bind6;
23 | char disable_ipv6;
24 | int sleep_ns;
25 | char always_shared;
26 | char disable_cmpfb;
27 | char *desktop_name;
28 | char *password_file;
29 | };
30 |
31 | struct kmsvnc_data
32 | {
33 | char *debug_capture_fb;
34 | char *card;
35 | char *force_driver;
36 | struct vnc_opt *vnc_opt;
37 | char input_wakeup;
38 | char disable_input;
39 | int va_derive_enabled;
40 | char debug_enabled;
41 | int source_plane;
42 | int source_crtc;
43 | int input_width;
44 | int input_height;
45 | int input_offx;
46 | int input_offy;
47 | char screen_blank;
48 | char screen_blank_restore;
49 | char va_byteorder_swap;
50 | struct kmsvnc_drm_data *drm;
51 | struct kmsvnc_input_data *input;
52 | struct kmsvnc_keymap_data *keymap;
53 | struct kmsvnc_va_data *va;
54 | rfbScreenInfoPtr server;
55 | char shutdown;
56 | char capture_cursor;
57 | char *cursor_bitmap;
58 | int cursor_bitmap_len;
59 | char *buf;
60 | char *buf1;
61 | };
62 |
63 |
64 |
65 | struct key_iter_search
66 | {
67 | xkb_keysym_t keysym;
68 |
69 | xkb_keycode_t keycode;
70 | xkb_level_index_t level;
71 | };
72 |
73 | struct kmsvnc_keymap_data
74 | {
75 | struct xkb_context *ctx;
76 | struct xkb_keymap *map;
77 | };
78 |
79 |
80 | struct kmsvnc_input_data {
81 | int uinput_fd;
82 | char *keystate;
83 | };
84 |
85 |
86 | struct kmsvnc_drm_funcs
87 | {
88 | void (*sync_start)(int);
89 | void (*sync_end)(int);
90 | void (*convert)(const char *, int, int, char *);
91 | };
92 |
93 | struct kmsvnc_drm_gamma_data
94 | {
95 | uint32_t size;
96 | uint16_t *red;
97 | uint16_t *green;
98 | uint16_t *blue;
99 | };
100 |
101 | struct kmsvnc_drm_data
102 | {
103 | int drm_fd;
104 | int drm_master_fd;
105 | drmVersionPtr drm_ver;
106 | int prime_fd;
107 | drmModePlane *plane;
108 | drmModePlane *cursor_plane;
109 | drmModePlaneRes *plane_res;
110 | drmModeFB2 *mfb;
111 | drmModeFB2 *cursor_mfb;
112 | uint32_t plane_id;
113 | int mmap_fd;
114 | size_t mmap_size;
115 | off_t mmap_offset;
116 | char *mapped;
117 | char *cursor_mapped;
118 | size_t cursor_mmap_size;
119 | char skip_map;
120 | struct kmsvnc_drm_funcs *funcs;
121 | char *pixfmt_name;
122 | char *mod_vendor;
123 | char *mod_name;
124 | char *kms_convert_buf;
125 | size_t kms_convert_buf_len;
126 | char *kms_cpy_tmp_buf;
127 | size_t kms_cpy_tmp_buf_len;
128 | char *kms_cursor_buf;
129 | size_t kms_cursor_buf_len;
130 | struct kmsvnc_drm_gamma_data *gamma;
131 | };
132 |
133 | struct kmsvnc_va_data
134 | {
135 | VADisplay dpy;
136 | int render_node_fd;
137 | VASurfaceID surface_id;
138 | VAImage *image;
139 | char *imgbuf;
140 | char derive_enabled;
141 | VAImageFormat* img_fmts;
142 | int img_fmt_count;
143 | VAImageFormat* selected_fmt;
144 | const char *vendor_string;
145 | };
146 |
147 | #define KMSVNC_FATAL(...) do{ fprintf(stderr, __VA_ARGS__); return 1; } while(0)
148 | #define KMSVNC_ARRAY_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
149 | #define KMSVNC_FOURCC_TO_INT(a,b,c,d) (((a) << 0) + ((b) << 8) + ((c) << 16) + ((d) << 24))
150 | #define KMSVNC_WRITE_MAY(fd,buf,count) do { ssize_t e = write((fd), (buf), (count)); if (e != (count)) fprintf(stderr, "should write %ld bytes, actually wrote %ld, on line %d\n", (count), e, __LINE__); } while (0)
151 |
152 | #define KMSVNC_DEBUG(...) do{ if (kmsvnc->debug_enabled) fprintf(stdout, __VA_ARGS__); } while(0)
153 |
154 | #define likely(x) __builtin_expect(!!(x), 1)
155 | #define unlikely(x) __builtin_expect(!!(x), 0)
156 |
--------------------------------------------------------------------------------
/va.c:
--------------------------------------------------------------------------------
1 | #define _GNU_SOURCE
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "va.h"
12 | #include "kmsvnc.h"
13 |
14 | extern struct kmsvnc_data *kmsvnc;
15 |
16 | void va_cleanup() {
17 | VAStatus s;
18 | if (kmsvnc->va) {
19 | if (kmsvnc->va->img_fmts) {
20 | free(kmsvnc->va->img_fmts);
21 | kmsvnc->va->img_fmts = NULL;
22 | }
23 | if (kmsvnc->va->imgbuf) {
24 | VA_MAY(vaUnmapBuffer(kmsvnc->va->dpy, kmsvnc->va->image->buf));
25 | kmsvnc->va->imgbuf = NULL;
26 | }
27 | if (kmsvnc->va->image) {
28 | if ((s = vaDestroyImage(kmsvnc->va->dpy, kmsvnc->va->image->image_id)) == VA_STATUS_SUCCESS) {
29 | free(kmsvnc->va->image);
30 | }
31 | VA_MAY(s);
32 | kmsvnc->va->image = NULL;
33 | }
34 | if (kmsvnc->va->surface_id > 0) {
35 | VA_MAY(vaDestroySurfaces(kmsvnc->va->dpy, &kmsvnc->va->surface_id, 1));
36 | kmsvnc->va->surface_id = 0;
37 | }
38 | if (kmsvnc->va->dpy) {
39 | VA_MAY(vaTerminate(kmsvnc->va->dpy));
40 | kmsvnc->va->dpy = NULL;
41 | }
42 | if (kmsvnc->va->vendor_string) {
43 | kmsvnc->va->vendor_string = NULL;
44 | }
45 | free(kmsvnc->va);
46 | kmsvnc->va = NULL;
47 | }
48 | }
49 |
50 | static void va_msg_callback(void *user_context, const char *message) {
51 | if (kmsvnc->debug_enabled) {
52 | printf("va msg: %s", message);
53 | }
54 | }
55 |
56 | static void va_error_callback(void *user_context, const char *message) {
57 | printf("va error: %s", message);
58 | }
59 |
60 | static char* fourcc_to_str(int fourcc) {
61 | static char ret[5];
62 | ret[4] = 0;
63 | for (int i = 0; i < 4; i++) {
64 | ret[i] = fourcc >> 8*i & 0xff;
65 | }
66 | return ret;
67 | }
68 |
69 | static const struct {
70 | uint32_t drm_fourcc;
71 | uint32_t va_fourcc;
72 | uint32_t va_rt_format;
73 | char alpha;
74 | } va_format_map[] = {
75 | {KMSVNC_FOURCC_TO_INT('X', 'R', '2', '4'), KMSVNC_FOURCC_TO_INT('B', 'G', 'R', 'X'), VA_RT_FORMAT_RGB32, 0},
76 | {KMSVNC_FOURCC_TO_INT('A', 'R', '2', '4'), KMSVNC_FOURCC_TO_INT('B', 'G', 'R', 'A'), VA_RT_FORMAT_RGB32, 1},
77 | {KMSVNC_FOURCC_TO_INT('X', 'R', '3', '0'), KMSVNC_FOURCC_TO_INT('X', 'R', '3', '0'), VA_RT_FORMAT_RGB32_10, 0},
78 | {KMSVNC_FOURCC_TO_INT('A', 'R', '3', '0'), KMSVNC_FOURCC_TO_INT('A', 'R', '3', '0'), VA_RT_FORMAT_RGB32_10, 1},
79 | };
80 |
81 | struct va_fmt_data {
82 | uint32_t va_fourcc;
83 | VAImageFormat *fmt;
84 | char is_alpha;
85 | uint32_t va_rt_format;
86 | uint32_t depth;
87 | };
88 |
89 | static VAImageFormat* vaImgFmt_apply_quirks(struct va_fmt_data* data) {
90 | static VAImageFormat ret = {0};
91 | memcpy(&ret, data->fmt, sizeof(VAImageFormat));
92 | if ((kmsvnc->va_byteorder_swap ^ !strncmp(kmsvnc->va->vendor_string, "Mesa", 4)) && data->depth != 30) {
93 | printf("applying rgb mask byte order swap\n");
94 | ret.blue_mask = __builtin_bswap32(data->fmt->blue_mask);
95 | ret.green_mask = __builtin_bswap32(data->fmt->green_mask);
96 | ret.red_mask = __builtin_bswap32(data->fmt->red_mask);
97 | }
98 | return &ret;
99 | }
100 |
101 | static void print_va_image_fmt(VAImageFormat *fmt) {
102 | printf("image fmt: fourcc %d, %s, byte_order %s, bpp %d, depth %d, blue_mask %#x, green_mask %#x, red_mask %#x, alpha_mask %#x, reserved %#x %#x %#x %#x\n", fmt->fourcc,
103 | fourcc_to_str(fmt->fourcc),
104 | fmt->byte_order == 1 ? "VA_LSB_FIRST" : "VA_MSB_FIRST",
105 | fmt->bits_per_pixel,
106 | fmt->depth,
107 | fmt->blue_mask,
108 | fmt->green_mask,
109 | fmt->red_mask,
110 | fmt->alpha_mask,
111 | fmt->va_reserved[0],
112 | fmt->va_reserved[1],
113 | fmt->va_reserved[2],
114 | fmt->va_reserved[3]
115 | );
116 | }
117 |
118 | int va_init() {
119 | if (!kmsvnc->drm || !kmsvnc->drm->drm_fd || !kmsvnc->drm->prime_fd) {
120 | KMSVNC_FATAL("drm is not initialized\n");
121 | }
122 |
123 | setenv("DISPLAY", "", 1);
124 | setenv("WAYLAND_DISPLAY", "", 1);
125 |
126 | struct kmsvnc_va_data *va = malloc(sizeof(struct kmsvnc_va_data));
127 | if (!va) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
128 | memset(va, 0, sizeof(struct kmsvnc_va_data));
129 | kmsvnc->va = va;
130 |
131 | char* render_node;
132 | int effective_fd = 0;
133 | if ((render_node = drmGetRenderDeviceNameFromFd(kmsvnc->drm->drm_fd))) {
134 | va->render_node_fd = open(render_node, O_RDWR);
135 | free(render_node);
136 | }
137 | else {
138 | printf("Using non-render node because the device does not have an associated render node.\n");
139 | }
140 | if (va->render_node_fd > 0) {
141 | effective_fd = va->render_node_fd;
142 | }
143 | else {
144 | printf("Using non-render node because render node fails to open.\n");
145 | effective_fd = kmsvnc->drm->drm_fd;
146 | }
147 |
148 | va->dpy = vaGetDisplayDRM(effective_fd);
149 | if (!va->dpy) {
150 | KMSVNC_FATAL("vaGetDisplayDRM failed\n");
151 | }
152 |
153 | vaSetErrorCallback(va->dpy, &va_error_callback, NULL);
154 | vaSetInfoCallback(va->dpy, &va_msg_callback, NULL);
155 |
156 | int major, minor;
157 | VAStatus status;
158 | VA_MUST(vaInitialize(va->dpy, &major, &minor));
159 |
160 | va->vendor_string = vaQueryVendorString(va->dpy);
161 | printf("vaapi vendor %s\n", va->vendor_string);
162 |
163 | VADRMPRIMESurfaceDescriptor prime_desc;
164 | VASurfaceAttrib prime_attrs[2] = {
165 | {
166 | .type = VASurfaceAttribMemoryType,
167 | .flags = VA_SURFACE_ATTRIB_SETTABLE,
168 | .value.type = VAGenericValueTypeInteger,
169 | .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
170 | },
171 | {
172 | .type = VASurfaceAttribExternalBufferDescriptor,
173 | .flags = VA_SURFACE_ATTRIB_SETTABLE,
174 | .value.type = VAGenericValueTypePointer,
175 | .value.value.p = &prime_desc,
176 | }
177 | };
178 |
179 | uint32_t rt_format = 0;
180 | char is_alpha = 0;
181 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(va_format_map); i++) {
182 | if (kmsvnc->drm->mfb->pixel_format == va_format_map[i].drm_fourcc) {
183 | prime_desc.fourcc = va_format_map[i].va_fourcc;
184 | rt_format = va_format_map[i].va_rt_format;
185 | is_alpha = va_format_map[i].alpha;
186 | break;
187 | }
188 | }
189 | if (!rt_format) {
190 | KMSVNC_FATAL("Unsupported pixfmt %s for vaapi, please create an issue with your pixfmt.", kmsvnc->drm->pixfmt_name);
191 | }
192 | if (kmsvnc->debug_enabled) {
193 | printf("selected rt_format %u, alpha %d\n", rt_format, is_alpha);
194 | }
195 | prime_desc.width = kmsvnc->drm->mfb->width;
196 | prime_desc.height = kmsvnc->drm->mfb->height;
197 |
198 | int i;
199 | int max_size = 0;
200 | for (i = 0; i < 4; i++) {
201 | int size = kmsvnc->drm->mfb->offsets[i] + kmsvnc->drm->mfb->height * kmsvnc->drm->mfb->pitches[i];
202 | if (size > max_size) max_size = size;
203 | }
204 | for (i = 0; i < 4; i++) {
205 | prime_desc.objects[i].fd = kmsvnc->drm->prime_fd;
206 | prime_desc.objects[i].size = max_size;
207 | prime_desc.objects[i].drm_format_modifier = kmsvnc->drm->mfb->modifier;
208 | }
209 |
210 | prime_desc.num_layers = 1;
211 | prime_desc.layers[0].drm_format = kmsvnc->drm->mfb->pixel_format;
212 | for (i = 0; i < 4; i++) {
213 | prime_desc.layers[0].object_index[i] = 0;
214 | prime_desc.layers[0].offset[i] = kmsvnc->drm->mfb->offsets[i];
215 | prime_desc.layers[0].pitch[i] = kmsvnc->drm->mfb->pitches[i];
216 | }
217 | for (i = 0; i < 4; i++) {
218 | if (!kmsvnc->drm->mfb->handles[i]) {
219 | break;
220 | }
221 | }
222 | prime_desc.layers[0].num_planes = i;
223 | prime_desc.num_objects = 1;
224 |
225 | VAStatus s;
226 | if ((s = vaCreateSurfaces(va->dpy, rt_format,
227 | kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, &va->surface_id, 1,
228 | prime_attrs, KMSVNC_ARRAY_ELEMENTS(prime_attrs))) != VA_STATUS_SUCCESS)
229 | {
230 | printf("vaCreateSurfaces prime2 error %#x %s, trying prime\n", s, vaErrorStr(s));
231 |
232 | VASurfaceAttribExternalBuffers buffer_desc;
233 | VASurfaceAttrib buffer_attrs[2] = {
234 | {
235 | .type = VASurfaceAttribMemoryType,
236 | .flags = VA_SURFACE_ATTRIB_SETTABLE,
237 | .value.type = VAGenericValueTypeInteger,
238 | .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
239 | },
240 | {
241 | .type = VASurfaceAttribExternalBufferDescriptor,
242 | .flags = VA_SURFACE_ATTRIB_SETTABLE,
243 | .value.type = VAGenericValueTypePointer,
244 | .value.value.p = &buffer_desc,
245 | }
246 | };
247 |
248 | unsigned long fd = kmsvnc->drm->prime_fd;
249 |
250 | buffer_desc.pixel_format = prime_desc.fourcc;
251 | buffer_desc.width = kmsvnc->drm->mfb->width;
252 | buffer_desc.height = kmsvnc->drm->mfb->height;
253 | buffer_desc.data_size = max_size;
254 | buffer_desc.buffers = &fd;
255 | buffer_desc.num_buffers = 1;
256 | buffer_desc.flags = 0;
257 |
258 | for (i = 0; i < 4; i++) {
259 | buffer_desc.pitches[i] = kmsvnc->drm->mfb->pitches[i];
260 | buffer_desc.offsets[i] = kmsvnc->drm->mfb->offsets[i];
261 | }
262 | buffer_desc.num_planes = prime_desc.layers[0].num_planes;
263 |
264 |
265 | VA_MUST(vaCreateSurfaces(va->dpy, rt_format,
266 | kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, &va->surface_id, 1,
267 | buffer_attrs, KMSVNC_ARRAY_ELEMENTS(buffer_attrs)));
268 | }
269 |
270 |
271 | va->img_fmt_count = vaMaxNumImageFormats(va->dpy);
272 | va->img_fmts = malloc(sizeof(VAImageFormat) * va->img_fmt_count);
273 | if (!va->img_fmts) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
274 | {
275 | int got;
276 | vaQueryImageFormats(va->dpy, va->img_fmts, &got);
277 | if (got != va->img_fmt_count) {
278 | printf("got less VAImageFormats, %d instead of %d\n", got, va->img_fmt_count);
279 | va->img_fmt_count = got;
280 | }
281 | }
282 |
283 | if (kmsvnc->debug_enabled) {
284 | for (int i = 0; i < va->img_fmt_count; i++) {
285 | print_va_image_fmt(va->img_fmts + i);
286 | }
287 | }
288 |
289 | struct va_fmt_data format_to_try[] = {
290 | {KMSVNC_FOURCC_TO_INT('R','G','B','X'), NULL, 0, VA_RT_FORMAT_RGB32, 24},
291 | {KMSVNC_FOURCC_TO_INT('R','G','B','A'), NULL, 1, VA_RT_FORMAT_RGB32, 32},
292 |
293 | {KMSVNC_FOURCC_TO_INT('X','B','G','R'), NULL, 0, VA_RT_FORMAT_RGB32, 24},
294 | {KMSVNC_FOURCC_TO_INT('A','B','G','R'), NULL, 1, VA_RT_FORMAT_RGB32, 32},
295 |
296 | {KMSVNC_FOURCC_TO_INT('X','R','G','B'), NULL, 0, VA_RT_FORMAT_RGB32, 24},
297 | {KMSVNC_FOURCC_TO_INT('A','R','G','B'), NULL, 1, VA_RT_FORMAT_RGB32, 32},
298 |
299 | {KMSVNC_FOURCC_TO_INT('B','G','R','X'), NULL, 0, VA_RT_FORMAT_RGB32, 24},
300 | {KMSVNC_FOURCC_TO_INT('B','G','R','A'), NULL, 1, VA_RT_FORMAT_RGB32, 32},
301 |
302 |
303 | {KMSVNC_FOURCC_TO_INT('X','R','3','0'), NULL, 0, VA_RT_FORMAT_RGB32_10, 30},
304 | {KMSVNC_FOURCC_TO_INT('A','R','3','0'), NULL, 1, VA_RT_FORMAT_RGB32_10, 30},
305 | {KMSVNC_FOURCC_TO_INT('X','B','3','0'), NULL, 0, VA_RT_FORMAT_RGB32_10, 30},
306 | {KMSVNC_FOURCC_TO_INT('A','B','3','0'), NULL, 1, VA_RT_FORMAT_RGB32_10, 30},
307 | };
308 |
309 | for (int i = 0; i < va->img_fmt_count; i++) {
310 | for (int j = 0; j < KMSVNC_ARRAY_ELEMENTS(format_to_try); j++) {
311 | if (va->img_fmts[i].fourcc == format_to_try[j].va_fourcc) {
312 | format_to_try[j].fmt = va->img_fmts + i;
313 | }
314 | }
315 | }
316 |
317 | va->image = malloc(sizeof(VAImage));
318 | if (!va->image) KMSVNC_FATAL("memory allocation error at %s:%d\n", __FILE__, __LINE__);
319 |
320 | va->derive_enabled = 0;
321 | va->derive_enabled = kmsvnc->va_derive_enabled < 0 ? va->derive_enabled : kmsvnc->va_derive_enabled != 0;
322 | if (va->derive_enabled) {
323 | if ((s = vaDeriveImage(va->dpy, va->surface_id, va->image)) == VA_STATUS_SUCCESS) {
324 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(format_to_try); i++) {
325 | if (format_to_try[i].fmt == NULL) continue;
326 | if (va->image->format.fourcc == format_to_try[i].fmt->fourcc) {
327 | va->selected_fmt = vaImgFmt_apply_quirks(format_to_try + i);
328 | break;
329 | }
330 | }
331 | if (!va->selected_fmt) {
332 | va->derive_enabled = 0;
333 | printf("vaDeriveImage returned unknown fourcc %d %s\n", va->image->format.fourcc, fourcc_to_str(va->image->format.fourcc));
334 | VA_MAY(vaDestroyImage(kmsvnc->va->dpy, kmsvnc->va->image->image_id));
335 | }
336 | }
337 | VA_MAY(s);
338 | }
339 | if (va->derive_enabled) {
340 | if ((s = vaMapBuffer(va->dpy, va->image->buf, (void**)&va->imgbuf)) != VA_STATUS_SUCCESS) {
341 | VA_MAY(s);
342 | VA_MAY(vaDestroyImage(kmsvnc->va->dpy, kmsvnc->va->image->image_id));
343 | va->derive_enabled = 0;
344 | }
345 | }
346 | if (!va->derive_enabled) {
347 | for (int i = 0; i < KMSVNC_ARRAY_ELEMENTS(format_to_try); i++) {
348 | if (format_to_try[i].fmt == NULL) continue;
349 | if (!kmsvnc->debug_enabled && rt_format != format_to_try[i].va_rt_format) continue;
350 | if (is_alpha != format_to_try[i].is_alpha) continue;
351 |
352 | VAImageFormat *fmt = format_to_try[i].fmt;
353 | if ((s = vaCreateImage(va->dpy, fmt, kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, va->image)) != VA_STATUS_SUCCESS) {
354 | VA_MAY(s);
355 | continue;
356 | }
357 | if ((s = vaMapBuffer(va->dpy, va->image->buf, (void**)&va->imgbuf)) != VA_STATUS_SUCCESS) {
358 | VA_MAY(s);
359 | VA_MAY(vaDestroyImage(kmsvnc->va->dpy, kmsvnc->va->image->image_id));
360 | continue;
361 | }
362 | if ((s = vaGetImage(kmsvnc->va->dpy, kmsvnc->va->surface_id, 0, 0,
363 | kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height,
364 | kmsvnc->va->image->image_id)) != VA_STATUS_SUCCESS)
365 | {
366 | VA_MAY(s);
367 | VA_MAY(vaUnmapBuffer(kmsvnc->va->dpy, kmsvnc->va->image->buf));
368 | VA_MAY(vaDestroyImage(kmsvnc->va->dpy, kmsvnc->va->image->image_id));
369 | continue;
370 | }
371 | else {
372 | va->selected_fmt = vaImgFmt_apply_quirks(format_to_try + i);
373 | break;
374 | }
375 | }
376 | if (!va->selected_fmt) {
377 | va->imgbuf = NULL;
378 | KMSVNC_FATAL("failed to get vaapi image\n");
379 | }
380 | }
381 | printf("got vaapi %simage:\n", va->derive_enabled ? "derive " : "");
382 | print_va_image_fmt(&va->image->format);
383 | if (kmsvnc->debug_enabled) {
384 | fprintf(stderr, "selected image format:\n");
385 | print_va_image_fmt(va->selected_fmt);
386 | }
387 | return 0;
388 | }
389 |
390 | int va_hwframe_to_vaapi(char *out) {
391 | if (!kmsvnc->va->derive_enabled) {
392 | VA_MUST(vaGetImage(kmsvnc->va->dpy, kmsvnc->va->surface_id, 0, 0,
393 | kmsvnc->drm->mfb->width, kmsvnc->drm->mfb->height, kmsvnc->va->image->image_id));
394 | }
395 | memcpy(out, kmsvnc->va->imgbuf, kmsvnc->drm->mfb->width * kmsvnc->drm->mfb->height * BYTES_PER_PIXEL);
396 | return 0;
397 | }
398 |
--------------------------------------------------------------------------------
/va.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define VA_MUST(x) do{VAStatus _s; if ((_s = (x)) != VA_STATUS_SUCCESS) KMSVNC_FATAL("va operation error %#x %s on line %d\n", _s, vaErrorStr(_s), __LINE__); } while (0)
4 | #define VA_MAY(x) do{VAStatus _s; if ((_s = (x)) != VA_STATUS_SUCCESS) fprintf(stderr, "va operation error %#x %s on line %d\n", _s, vaErrorStr(_s), __LINE__); } while (0)
5 |
6 | void va_cleanup();
7 | int va_init();
8 | int va_hwframe_to_vaapi(char *out);
9 |
--------------------------------------------------------------------------------