summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesus Fernandez <jesus.fernandez@qt.io>2017-06-07 11:13:52 +0200
committerJesus Fernandez <Jesus.Fernandez@qt.io>2017-06-27 18:00:22 +0000
commit91bc2d2f217507f20933b7caf65dc33701d2e9cd (patch)
treefa214c2537fbbb18109f0309fc1f3c278c0154ba
parent440a7710219674ed9e0e4d1396853fb1f7d35107 (diff)
Add Qt WebGL platform plugin
Done-with: Michael Winkelmann <michael.winkelmann@qt.io> Change-Id: I6632475956393116af8885f42ba557e35d2b0985 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
-rw-r--r--.qmake.conf4
-rw-r--r--LICENSE.GPL3674
-rw-r--r--qtwebglplugin.pro3
-rw-r--r--src/plugins/platforms/platforms.pro3
-rw-r--r--src/plugins/platforms/webgl/favicon.icobin0 -> 1150 bytes
-rw-r--r--src/plugins/platforms/webgl/index.html17
-rw-r--r--src/plugins/platforms/webgl/qwebglcontext.cpp2520
-rw-r--r--src/plugins/platforms/webgl/qwebglcontext.h71
-rw-r--r--src/plugins/platforms/webgl/qwebglfunctioncall.cpp150
-rw-r--r--src/plugins/platforms/webgl/qwebglfunctioncall.h77
-rw-r--r--src/plugins/platforms/webgl/qwebglhttpserver.cpp411
-rw-r--r--src/plugins/platforms/webgl/qwebglhttpserver.h75
-rw-r--r--src/plugins/platforms/webgl/qwebglintegration.cpp645
-rw-r--r--src/plugins/platforms/webgl/qwebglintegration.h79
-rw-r--r--src/plugins/platforms/webgl/qwebglintegration_p.h126
-rw-r--r--src/plugins/platforms/webgl/qwebglmain.cpp75
-rw-r--r--src/plugins/platforms/webgl/qwebglplatformservices.cpp45
-rw-r--r--src/plugins/platforms/webgl/qwebglplatformservices.h52
-rw-r--r--src/plugins/platforms/webgl/qwebglscreen.cpp109
-rw-r--r--src/plugins/platforms/webgl/qwebglscreen.h70
-rw-r--r--src/plugins/platforms/webgl/qwebglwebsocketserver.cpp310
-rw-r--r--src/plugins/platforms/webgl/qwebglwebsocketserver.h90
-rw-r--r--src/plugins/platforms/webgl/qwebglwindow.cpp165
-rw-r--r--src/plugins/platforms/webgl/qwebglwindow.h76
-rw-r--r--src/plugins/platforms/webgl/qwebglwindow_p.h75
-rw-r--r--src/plugins/platforms/webgl/webgl.json3
-rw-r--r--src/plugins/platforms/webgl/webgl.pro45
-rw-r--r--src/plugins/platforms/webgl/webgl.qrc7
-rw-r--r--src/plugins/platforms/webgl/webqt.jsx1212
-rw-r--r--src/plugins/plugins.pro4
-rw-r--r--src/src.pro4
-rw-r--r--sync.profile6
-rw-r--r--tests/tests.pro2
33 files changed, 7205 insertions, 0 deletions
diff --git a/.qmake.conf b/.qmake.conf
new file mode 100644
index 0000000..be2d3fa
--- /dev/null
+++ b/.qmake.conf
@@ -0,0 +1,4 @@
+load(qt_build_config)
+
+MODULE_VERSION = 5.10.0
+
diff --git a/LICENSE.GPL3 b/LICENSE.GPL3
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENSE.GPL3
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/qtwebglplugin.pro b/qtwebglplugin.pro
new file mode 100644
index 0000000..384a14a
--- /dev/null
+++ b/qtwebglplugin.pro
@@ -0,0 +1,3 @@
+requires(!winrt:qtHaveModule(websockets):qtHaveModule(gui):qtConfig(opengles2))
+
+load(qt_parts)
diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro
new file mode 100644
index 0000000..79b1eca
--- /dev/null
+++ b/src/plugins/platforms/platforms.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += webgl
diff --git a/src/plugins/platforms/webgl/favicon.ico b/src/plugins/platforms/webgl/favicon.ico
new file mode 100644
index 0000000..fa5275a
--- /dev/null
+++ b/src/plugins/platforms/webgl/favicon.ico
Binary files differ
diff --git a/src/plugins/platforms/webgl/index.html b/src/plugins/platforms/webgl/index.html
new file mode 100644
index 0000000..c1555af
--- /dev/null
+++ b/src/plugins/platforms/webgl/index.html
@@ -0,0 +1,17 @@
+<html>
+ <head profile="http://www.w3.org/2005/10/profile">
+ <link rel="icon" type="image/png" href="/favicon.png">
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+ <title>QtWebGL - Waiting for connection...</title>
+ <script>
+ var startTime = (new Date()).getTime();
+ </script>
+ <script type="text/javascript" src="webqt.js"></script>
+ <script>
+ var endTime = (new Date()).getTime();
+ console.log("Took " + (endTime - startTime) + " milliseconds to parse and execute");
+ </script>
+ </head>
+ <body />
+</html>
diff --git a/src/plugins/platforms/webgl/qwebglcontext.cpp b/src/plugins/platforms/webgl/qwebglcontext.cpp
new file mode 100644
index 0000000..afa5368
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglcontext.cpp
@@ -0,0 +1,2520 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglcontext.h"
+
+#include "qwebglfunctioncall.h"
+#include "qwebglintegration.h"
+#include "qwebglintegration_p.h"
+#include "qwebglwebsocketserver.h"
+#include "qwebglwindow.h"
+#include "qwebglwindow_p.h"
+
+#include <QtCore/qhash.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qset.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qimage.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qsurface.h>
+#include <QtWebSockets/qwebsocket.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.context")
+
+struct PixelStorageModes
+{
+ PixelStorageModes() : unpackAlignment(4) { }
+ int unpackAlignment;
+};
+
+struct ContextData {
+ GLuint currentProgram = 0;
+ GLuint boundArrayBuffer = 0;
+ GLuint boundElementArrayBuffer = 0;
+ GLuint boundTexture2D = 0;
+ GLenum activeTextureUnit = GL_TEXTURE0;
+ GLuint boundDrawFramebuffer = 0;
+// GLuint boundReadFramebuffer = 0;
+ GLuint unpackAlignment = 4;
+ struct VertexAttrib {
+ VertexAttrib() : arrayBufferBinding(0), pointer(0), enabled(false) { }
+ GLuint arrayBufferBinding;
+ void *pointer;
+ bool enabled;
+ GLint size;
+ GLenum type;
+ bool normalized;
+ GLsizei stride;
+ };
+ QHash<GLuint, VertexAttrib> vertexAttribPointers;
+ QHash<GLuint, QImage> images;
+ PixelStorageModes pixelStorage;
+ QMap<GLenum, QVariant> cachedParameters;
+ QSet<QByteArray> stringCache;
+};
+
+static QHash<int, ContextData> s_contextData;
+
+QWebGLContext *currentContext()
+{
+ auto context = QOpenGLContext::currentContext();
+ if (context)
+ return static_cast<QWebGLContext *>(context->handle());
+ return nullptr;
+}
+
+ContextData *currentContextData()
+{
+ auto context = currentContext();
+ if (context)
+ return &s_contextData[context->id()];
+ return nullptr;
+}
+
+inline int imageSize(GLsizei width, GLsizei height, GLenum format, GLenum type,
+ const PixelStorageModes &pixelStorage)
+{
+ Q_UNUSED(pixelStorage); // TODO: Support different pixelStorage formats
+
+ static struct BppTabEntry {
+ GLenum format;
+ GLenum type;
+ int bytesPerPixel;
+ } bppTab[] = {
+ { GL_RGBA, GL_UNSIGNED_BYTE, 4 },
+ { GL_RGBA, GL_BYTE, 4 },
+ { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 2 },
+ { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 2 },
+ { GL_RGBA, GL_FLOAT, 16 },
+ { GL_RGB, GL_UNSIGNED_BYTE, 3 },
+ { GL_RGB, GL_BYTE, 3 },
+ { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2 },
+ { GL_RGB, GL_FLOAT, 12 },
+
+ { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 2 },
+ { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 4 },
+ { GL_DEPTH_COMPONENT, GL_FLOAT, 4 },
+
+ { GL_RGBA, GL_UNSIGNED_BYTE, 4 },
+ { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 2 },
+ { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 2 },
+ { GL_RGB, GL_UNSIGNED_BYTE, 3 },
+ { GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 2 },
+ { GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 1 },
+ { GL_LUMINANCE, GL_UNSIGNED_BYTE, 1 },
+ { GL_ALPHA, GL_UNSIGNED_BYTE, 1 },
+
+ { GL_BGRA_EXT, GL_UNSIGNED_BYTE, 4 },
+ { GL_BGRA_EXT, GL_BYTE, 4 },
+ { GL_BGRA_EXT, GL_UNSIGNED_SHORT_4_4_4_4, 2 },
+ { GL_BGRA_EXT, GL_UNSIGNED_SHORT_5_5_5_1, 2 },
+ { GL_BGRA_EXT, GL_FLOAT, 16 }
+ };
+
+ int bytesPerPixel = 0;
+ for (size_t i = 0; i < sizeof(bppTab) / sizeof(BppTabEntry); ++i) {
+ if (bppTab[i].format == format && bppTab[i].type == type) {
+ bytesPerPixel = bppTab[i].bytesPerPixel;
+ break;
+ }
+ }
+
+ const int rowSize = width * bytesPerPixel;
+ if (!bytesPerPixel)
+ qCWarning(lc, "Unknown texture format %x - %x", format, type);
+
+ return rowSize * height;
+}
+
+QByteArrayList strings;
+
+static void lockMutex()
+{
+ QWebGLIntegrationPrivate::instance()->webSocketServer->mutex()->lock();
+}
+
+static void waitCondition(unsigned long time = ULONG_MAX)
+{
+ auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex();
+ auto waitCondition = QWebGLIntegrationPrivate::instance()->webSocketServer->waitCondition();
+ waitCondition->wait(mutex, time);
+}
+
+static void unlockMutex()
+{
+ auto mutex = QWebGLIntegrationPrivate::instance()->webSocketServer->mutex();
+ mutex->unlock();
+}
+
+static QVariant queryValue(int id)
+{
+ return QWebGLContext::queryValue(id);
+}
+
+static int elementSize(GLenum type)
+{
+ switch (type) {
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ return 2;
+ case GL_FLOAT:
+ case GL_FIXED:
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ return 4;
+ default:
+ return 1;
+ }
+}
+
+static int vertexSize(GLint elementsPerVertex, GLenum type)
+{
+ return elementSize(type) * elementsPerVertex;
+}
+
+static int bufferSize(GLsizei count, GLint elemsPerVertex, GLenum type, GLsizei stride)
+{
+ if (count == 0)
+ return 0;
+
+ int vsize = vertexSize(elemsPerVertex, type);
+
+ if (stride == 0)
+ stride = vsize;
+
+ return vsize + (count - 1) * stride;
+}
+
+static void setVertexAttribs(QWebGLFunctionCall *event, GLsizei count)
+{
+ event->addInt(currentContextData()->vertexAttribPointers.count());
+ QHashIterator<GLuint, ContextData::VertexAttrib> it(currentContextData()->vertexAttribPointers);
+ while (it.hasNext()) {
+ it.next();
+ const ContextData::VertexAttrib &va(it.value());
+ if (va.arrayBufferBinding == 0 && va.enabled) {
+ int len = bufferSize(count, va.size, va.type, va.stride);
+ event->addUInt(it.key());
+ event->addInt(va.size);
+ event->addInt(va.type);
+ event->addInt(va.normalized);
+ event->addInt(va.stride);
+ // found an enabled vertex attribute that was specified with a client-side pointer
+ event->addData(QByteArray((const char *)va.pointer, len));
+ }
+ }
+}
+
+namespace QWebGL {
+
+static void glActiveTexture(GLenum texture)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("activeTexture"));
+ if (!event)
+ return;
+ event->addInt(texture);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ currentContextData()->activeTextureUnit = texture;
+}
+
+static void glAttachShader(GLuint program, GLuint shader)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("attachShader"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ event->addUInt(shader);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBindAttribLocation(GLuint program, GLuint index, const GLchar * name)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bindAttribLocation"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ event->addUInt(index);
+ event->addString(name);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBindBuffer(GLenum target, GLuint buffer)
+{
+ if (target == GL_ARRAY_BUFFER)
+ currentContextData()->boundArrayBuffer = buffer;
+ if (target == GL_ELEMENT_ARRAY_BUFFER)
+ currentContextData()->boundElementArrayBuffer = buffer;
+
+ auto event = currentContext()->createEvent(QStringLiteral("bindBuffer"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addUInt(buffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBindFramebuffer(GLenum target, GLuint framebuffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bindFramebuffer"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addUInt(framebuffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+
+ if (target == GL_FRAMEBUFFER)
+ currentContextData()->boundDrawFramebuffer = framebuffer;
+}
+
+static void glBindRenderbuffer(GLenum target, GLuint renderbuffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bindRenderbuffer"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addUInt(renderbuffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBindTexture(GLenum target, GLuint texture)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bindTexture"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addUInt(texture);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ if (target == GL_TEXTURE_2D)
+ currentContextData()->boundTexture2D = texture;
+}
+
+static void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blendColor"));
+ if (!event)
+ return;
+ event->addFloat(red);
+ event->addFloat(green);
+ event->addFloat(blue);
+ event->addFloat(alpha);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBlendEquation(GLenum mode)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blendEquation"));
+ if (!event)
+ return;
+ event->addInt(mode);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blendEquationSeparate"));
+ if (!event)
+ return;
+ event->addInt(modeRGB);
+ event->addInt(modeAlpha);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBlendFunc(GLenum sfactor, GLenum dfactor)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blendFunc"));
+ if (!event)
+ return;
+ event->addInt(sfactor);
+ event->addInt(dfactor);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha,
+ GLenum dfactorAlpha)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blendFuncSeparate"));
+ if (!event)
+ return;
+ event->addInt(sfactorRGB);
+ event->addInt(dfactorRGB);
+ event->addInt(sfactorAlpha);
+ event->addInt(dfactorAlpha);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBufferData(GLenum target, GLsizeiptr size, const void * data, GLenum usage)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bufferData"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(usage);
+ event->addInt(size);
+ if (data)
+ event->addData(QByteArray((const char *)data, size));
+ else
+ event->addData(QByteArray());
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void * data)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("bufferSubData"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(size);
+ event->addInt(offset);
+ event->addData(QByteArray((const char *)data, size));
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static GLenum glCheckFramebufferStatus(GLenum target)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("checkFramebufferStatus"), true);
+ if (!event) return -1;
+ const auto id = event->id();
+ event->addInt(target);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ auto value = queryValue(id).toInt();
+ return value;
+}
+
+static void glClear(GLbitfield mask)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("clear"));
+ if (!event)
+ return;
+ event->addInt(mask);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("clearColor"));
+ if (!event)
+ return;
+ event->addFloat(red);
+ event->addFloat(green);
+ event->addFloat(blue);
+ event->addFloat(alpha);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glClearDepthf(GLfloat d)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("clearDepthf"));
+ if (!event)
+ return;
+ event->addFloat(d);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glClearStencil(GLint s)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("clearStencil"));
+ if (!event)
+ return;
+ event->addInt(s);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("colorMask"));
+ if (!event)
+ return;
+ event->addInt(red);
+ event->addInt(green);
+ event->addInt(blue);
+ event->addInt(alpha);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glCompileShader(GLuint shader)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("compileShader"));
+ if (!event)
+ return;
+ event->addUInt(shader);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLsizei imageSize, const void * data)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("compressedTexImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(internalformat);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(border);
+ event->addInt(imageSize);
+ event->addData(QByteArray((const char *) data, imageSize));
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height, GLenum format,
+ GLsizei imageSize, const void * data)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("compressedTexSubImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(xoffset);
+ event->addInt(yoffset);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(format);
+ event->addInt(imageSize);
+ event->addData(QByteArray((const char *) data, imageSize));
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
+ GLint y, GLsizei width, GLsizei height, GLint border)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("copyTexImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(internalformat);
+ event->addInt(x);
+ event->addInt(y);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(border);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("copyTexSubImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(xoffset);
+ event->addInt(yoffset);
+ event->addInt(x);
+ event->addInt(y);
+ event->addInt(width);
+ event->addInt(height);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static GLuint glCreateProgram()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("createProgram"), true);
+ if (!event) return -1;
+ const auto id = event->id();
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toUInt();
+ return value;
+}
+
+static GLuint glCreateShader(GLenum type)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("createShader"), true);
+ if (!event) return -1;
+ const auto id = event->id();
+ event->addInt(type);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toUInt();
+ return value;
+}
+
+static void glCullFace(GLenum mode)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("cullFace"));
+ if (!event)
+ return;
+ event->addInt(mode);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteBuffers(GLsizei n, const GLuint * buffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteBuffers"));
+ if (!event)
+ return;
+ event->addInt(n);
+ for (int i = 0; i < n; ++i) {
+ event->addUInt(buffers[i]);
+ if (currentContextData()->boundArrayBuffer == buffers[i])
+ currentContextData()->boundArrayBuffer = 0;
+ if (currentContextData()->boundElementArrayBuffer == buffers[i])
+ currentContextData()->boundElementArrayBuffer = 0;
+ }
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteFramebuffers(GLsizei n, const GLuint * framebuffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteFramebuffers"));
+ if (!event)
+ return;
+ event->addInt(n);
+ for (int i = 0; i < n; ++i)
+ event->addUInt(framebuffers[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteProgram(GLuint program)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteProgram"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteRenderbuffers(GLsizei n, const GLuint * renderbuffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteRenderbuffers"));
+ if (!event)
+ return;
+ event->addInt(n);
+ for (int i = 0; i < n; ++i)
+ event->addUInt(renderbuffers[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteShader(GLuint shader)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteShader"));
+ if (!event)
+ return;
+ event->addUInt(shader);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDeleteTextures(GLsizei n, const GLuint * textures)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("deleteTextures"));
+ if (!event)
+ return;
+ event->addInt(n);
+ for (int i = 0; i < n; ++i)
+ event->addUInt(textures[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDepthFunc(GLenum func)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("depthFunc"));
+ if (!event)
+ return;
+ event->addInt(func);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDepthMask(GLboolean flag)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("depthMask"));
+ if (!event)
+ return;
+ event->addInt(flag);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDepthRangef(GLfloat n, GLfloat f)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("depthRangef"));
+ if (!event)
+ return;
+ event->addFloat(n);
+ event->addFloat(f);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDetachShader(GLuint program, GLuint shader)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("detachShader"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ event->addUInt(shader);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDisableVertexAttribArray(GLuint index)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("disableVertexAttribArray"));
+ if (!event)
+ return;
+ currentContextData()->vertexAttribPointers[index].enabled = false;
+ event->addUInt(index);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDrawArrays(GLenum mode, GLint first, GLsizei count)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("drawArrays"));
+ if (!event)
+ return;
+ event->addInt(mode);
+ event->addInt(first);
+ event->addInt(count);
+ // Some vertex attributes may be client-side, others may not. Therefore
+ // client-side ones need to transfer the data starting from the base
+ // pointer, not just from 'first'.
+ setVertexAttribs(event, first + count);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void * indices)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("drawElements"), false);
+ if (!event)
+ return;
+ event->addInt(mode);
+ event->addInt(count);
+ event->addInt(type);
+ setVertexAttribs(event, count);
+ ContextData *d = currentContextData();
+ if (d->boundElementArrayBuffer == 0) {
+ event->addInt(0);
+ QByteArray data((const char *) indices, count * elementSize(type));
+ event->addData(data.data());
+ } else {
+ event->addInt(1);
+ event->addUInt((quintptr) indices);
+ }
+
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glEnableVertexAttribArray(GLuint index)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("enableVertexAttribArray"));
+ if (!event)
+ return;
+ currentContextData()->vertexAttribPointers[index].enabled = true;
+ event->addUInt(index);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glFinish()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("finish"));
+ if (!event)
+ return;
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glFlush()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("flush"));
+ if (!event)
+ return;
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glFramebufferRenderbuffer(GLenum target, GLenum attachment,
+ GLenum renderbuffertarget, GLuint renderbuffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("framebufferRenderbuffer"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(attachment);
+ event->addInt(renderbuffertarget);
+ event->addUInt(renderbuffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget,
+ GLuint texture, GLint level)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("framebufferTexture2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(attachment);
+ event->addInt(textarget);
+ event->addUInt(texture);
+ event->addInt(level);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glFrontFace(GLenum mode)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("frontFace"));
+ if (!event)
+ return;
+ event->addInt(mode);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glGenBuffers(GLsizei n, GLuint* buffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("genBuffers"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(n);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toList();
+ if (values.size() != n) {
+ qCWarning(lc, "Failed to create buffers");
+ return;
+ }
+ for (int i = 0; i < n; ++i)
+ buffers[i] = values.at(i).toUInt();
+}
+
+static void glGenFramebuffers(GLsizei n, GLuint* framebuffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("genFramebuffers"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(n);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toList();
+ if (values.size() != n) {
+ qCWarning(lc, "Failed to create framebuffers");
+ return;
+ }
+ for (int i = 0; i < n; ++i)
+ framebuffers[i] = values.at(i).toUInt();
+}
+
+static void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("genRenderbuffers"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(n);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toList();
+ if (values.size() != n) {
+ qCWarning(lc, "Failed to create render buffers");
+ return;
+ }
+ for (int i = 0; i < n; ++i)
+ renderbuffers[i] = values.at(i).toUInt();
+}
+
+static void glGenTextures(GLsizei n, GLuint* textures)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("genTextures"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(n);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto variant = queryValue(id);
+ const auto values = variant.toList();
+ if (values.size() != n) {
+ qCWarning(lc, "Failed to create textures");
+ return;
+ }
+ for (int i = 0; i < n; ++i)
+ textures[i] = values.at(i).toUInt();
+}
+
+static void glGenerateMipmap(GLenum target)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("generateMipmap"));
+ if (!event)
+ return;
+ event->addInt(target);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length,
+ GLint* size, GLenum* type, GLchar* name)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getActiveAttrib"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addUInt(index);
+ event->addInt(bufSize);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toMap();
+ int rtype = values["rtype"].toInt();
+ int rsize = values["rsize"].toInt();
+ QByteArray rname = values["rname"].toByteArray();
+ if (type)
+ *type = rtype;
+ if (size)
+ *size = rsize;
+ int len = qMax(0, qMin(bufSize - 1, rname.size()));
+ if (length)
+ *length = len;
+ if (name) {
+ memcpy(name, rname.constData(), len);
+ name[len] = '\0';
+ }
+}
+
+static void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length,
+ GLint* size, GLenum* type, GLchar* name)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getActiveUniform"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addUInt(index);
+ event->addInt(bufSize);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toMap();
+ int rtype = values["rtype"].toInt();
+ int rsize = values["rtype"].toInt();
+ QByteArray rname = values["rtype"].toByteArray();
+ if (type)
+ *type = rtype;
+ if (size)
+ *size = rsize;
+ int len = qMax(0, qMin(bufSize - 1, rname.size()));
+ if (length)
+ *length = len;
+ if (name) {
+ memcpy(name, rname.constData(), len);
+ name[len] = '\0';
+ }
+}
+
+static void glGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count,
+ GLuint* shaders)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getAttachedShaders"),
+ true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addInt(maxCount);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto values = queryValue(id).toList();
+ *count = values.size();
+ for (int i = 0; i < values.size(); ++i)
+ shaders[i] = values.at(i).toUInt();
+}
+
+static GLint glGetAttribLocation(GLuint program, const GLchar * name)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getAttribLocation"), true);
+ if (!event) return -1;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addString(name);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static const GLubyte *glGetString(GLenum name)
+{
+ const auto it = currentContextData()->cachedParameters.find(name);
+ if (it != currentContextData()->cachedParameters.end()) {
+ auto &stringCache = currentContextData()->stringCache;
+ Q_ASSERT(it->type() == QVariant::String);
+ const auto string = it->toString().toLatin1();
+
+ {
+ auto it = stringCache.find(string), end = stringCache.end();
+ if (it == end)
+ it = stringCache.insert(string);
+ return (const GLubyte *)(it->constData());
+ }
+ }
+
+ auto event = currentContext()->createEvent(QStringLiteral("getString"), true);
+ if (!event) return nullptr;
+ const auto id = event->id();
+ event->addInt(name);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ auto value = queryValue(id).toByteArray();
+ qCDebug(lc, "glGetString: %x: %s", name, qPrintable(value));
+ strings.append(value);
+ return (const GLubyte *)strings.last().constData();
+}
+
+static void glGetIntegerv(GLenum pname, GLint* data)
+{
+ if (pname == GL_MAX_TEXTURE_SIZE) {
+ *data = 512;
+ return;
+ }
+
+ const auto it = currentContextData()->cachedParameters.find(pname);
+ if (it != currentContextData()->cachedParameters.end()) {
+ QList<QVariant> values;
+ switch (it->type()) {
+ case QVariant::Map: values = it->toMap().values(); break;
+ case QVariant::List: values = it->toList(); break;
+ default: values = QVariantList{ *it };
+ }
+ for (const auto integer : qAsConst(values)) {
+ bool ok;
+ *data = integer.toInt(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+ ++data;
+ }
+ return;
+ }
+
+ switch (pname) {
+ case GL_CURRENT_PROGRAM:
+ *data = currentContextData()->currentProgram;
+ return;
+ case GL_FRAMEBUFFER_BINDING:
+ *data = currentContextData()->boundDrawFramebuffer;
+ return;
+ case GL_ARRAY_BUFFER_BINDING:
+ *data = currentContextData()->boundArrayBuffer;
+ return;
+ case GL_ELEMENT_ARRAY_BUFFER_BINDING:
+ *data = currentContextData()->boundElementArrayBuffer;
+ return;
+ case GL_ACTIVE_TEXTURE:
+ *data = currentContextData()->activeTextureUnit;
+ return;
+ case GL_TEXTURE_BINDING_2D:
+ *data = currentContextData()->boundTexture2D;
+ return;
+ default:;
+ }
+
+ auto event = currentContext()->createEvent(QStringLiteral("getIntegerv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ qCDebug(lc, "glGetIntegerv: %x: %d", pname, value);
+ *data = value;
+}
+
+static void glGetBooleanv(GLenum pname, GLboolean* data)
+{
+ const auto it = currentContextData()->cachedParameters.find(pname);
+ if (it != currentContextData()->cachedParameters.end()) {
+ Q_ASSERT(it->type() == QVariant::Bool);
+ *data = it->toBool();
+ return;
+ }
+
+ auto event = currentContext()->createEvent(QStringLiteral("getBooleanv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ qCDebug(lc, "glGetBooleanv: %x, %d", pname, value);
+ *data = value;
+}
+
+static void glEnable(GLenum cap)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("enable"));
+ if (!event)
+ return;
+ event->addInt(cap);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+
+ auto it = currentContextData()->cachedParameters.find(cap);
+ if (it != currentContextData()->cachedParameters.end()) {
+ Q_ASSERT(it->type() == QVariant::Bool);
+ it->setValue(true);
+ }
+}
+
+static void glDisable(GLenum cap)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("disable"));
+ if (!event)
+ return;
+ event->addInt(cap);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+
+ auto it = currentContextData()->cachedParameters.find(cap);
+ if (it != currentContextData()->cachedParameters.end()) {
+ Q_ASSERT(it->type() == QVariant::Bool);
+ it->setValue(false);
+ }
+}
+
+static void glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getBufferParameteriv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(target);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ *params = value;
+}
+
+static GLenum glGetError()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getError"), true);
+ if (!event) return GL_NO_ERROR;
+ const auto id = event->id();
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static void glGetFloatv(GLenum pname, GLfloat* data)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getParameter"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toFloat(&ok);
+ qCDebug(lc, "glGetFloatv: %x, %f", pname, value);
+ if (!ok)
+ qCCritical(lc, "Invalid value");
+ else
+ *data = value;
+}
+
+static void glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment,
+ GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(
+ QStringLiteral("getFramebufferAttachmentParameteriv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(target);
+ event->addInt(attachment);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toInt(&ok);
+ if (!ok)
+ qCCritical(lc, "Invalid value");
+ else
+ *params = value;
+}
+
+static void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length,
+ GLchar* infoLog)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getProgramInfoLog"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addInt(bufSize);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toString();
+ *length = value.length();
+ if (bufSize >= value.length())
+ std::memcpy(infoLog, value.constData(), value.length());
+}
+
+static void glGetProgramiv(GLuint program, GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getProgramiv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ *params = queryValue(id).toInt();
+}
+
+static void glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getRenderbufferParameteriv"),
+ true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(target);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ *params = queryValue(id).toInt();
+}
+
+static void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getShaderInfoLog"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(shader);
+ event->addInt(bufSize);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toString();
+ *length = value.length();
+ if (bufSize >= value.length())
+ std::memcpy(infoLog, value.constData(), value.length());
+}
+
+static void glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range,
+ GLint* precision)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getShaderPrecisionFormat"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(shadertype);
+ event->addInt(precisiontype);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toMap();
+ range[0] = value.value(QStringLiteral("rangeMin")).toInt(&ok);
+ if (!ok)
+ qCCritical(lc, "Invalid rangeMin value");
+ range[1] = value.value(QStringLiteral("rangeMax")).toInt(&ok);
+ if (!ok)
+ qCCritical(lc, "Invalid rangeMax value");
+ *precision = value.value(QStringLiteral("precision")).toInt(&ok);
+ if (!ok)
+ qCCritical(lc, "Invalid precision value");
+}
+
+static void glGetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getShaderSource"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(shader);
+ event->addInt(bufSize);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toString().toLatin1();
+ *length = value.length();
+ if (bufSize >= value.length())
+ std::memcpy(source, value.constData(), value.length());
+}
+
+static void glGetShaderiv(GLuint shader, GLenum pname, GLint* params)
+{
+ if (pname == GL_INFO_LOG_LENGTH) {
+ GLsizei bufSize = 0;
+ glGetShaderInfoLog(shader, bufSize, &bufSize, nullptr);
+ *params = bufSize;
+ return;
+ }
+ if (pname == GL_SHADER_SOURCE_LENGTH) {
+ GLsizei bufSize = 0;
+ glGetShaderSource(shader, bufSize, &bufSize, nullptr);
+ *params = bufSize;
+ return;
+ }
+ auto event = currentContext()->createEvent(QStringLiteral("getShaderiv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(shader);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ *params = queryValue(id).toInt();
+}
+
+static void glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getTexParameterfv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(target);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ *params = queryValue(id).toFloat(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+}
+
+static void glGetTexParameteriv(GLenum target, GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getTexParameteriv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(target);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ *params = queryValue(id).toInt(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+}
+
+static GLint glGetUniformLocation(GLuint program, const GLchar * name)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getUniformLocation"), true);
+ if (!event) return -1;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addString(name);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toInt(&ok);
+ if (!ok) {
+ qCWarning(lc, "Failed to cast value");
+ return -1;
+ }
+ return value;
+}
+
+static void glGetUniformfv(GLuint program, GLint location, GLfloat* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getUniformfv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addInt(location);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toFloat(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+ *params = value;
+}
+
+static void glGetUniformiv(GLuint program, GLint location, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getUniformiv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(program);
+ event->addInt(location);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toInt(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+ *params = value;
+}
+
+static void glGetVertexAttribPointerv(GLuint index, GLenum pname, void ** pointer)
+{
+ Q_UNUSED(pointer);
+ qCCritical(lc, "Not supported");
+ return;
+ auto event = currentContext()->createEvent(QStringLiteral("getVertexAttribPointerv"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getVertexAttribfv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(index);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toFloat(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+ *params = value;
+}
+
+static void glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("getVertexAttribiv"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addUInt(index);
+ event->addInt(pname);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ bool ok;
+ const auto value = queryValue(id).toInt(&ok);
+ if (!ok)
+ qCWarning(lc, "Failed to cast value");
+ *params = value;
+}
+
+static void glHint(GLenum target, GLenum mode)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("hint"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(mode);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static GLboolean glIsBuffer(GLuint buffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isBuffer"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(buffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ auto value = queryValue(id).toInt();
+ return value;
+}
+
+static GLboolean glIsEnabled(GLenum cap)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isEnabled"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addInt(cap);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ auto value = queryValue(id).toInt();
+ return value;
+}
+
+static GLboolean glIsFramebuffer(GLuint framebuffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isFramebuffer"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(framebuffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static GLboolean glIsProgram(GLuint program)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isProgram"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(program);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static GLboolean glIsRenderbuffer(GLuint renderbuffer)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isRenderbuffer"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(renderbuffer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static GLboolean glIsShader(GLuint shader)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isShader"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(shader);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = QWebGLIntegrationPrivate::instance()->webSocketServer->queryValue(id);
+ return value.toInt();
+}
+
+static GLboolean glIsTexture(GLuint texture)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("isTexture"), true);
+ if (!event) return GL_FALSE;
+ const auto id = event->id();
+ event->addUInt(texture);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toInt();
+ return value;
+}
+
+static void glLineWidth(GLfloat width)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("lineWidth"));
+ if (!event)
+ return;
+ event->addFloat(width);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glLinkProgram(GLuint program)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("linkProgram"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glPixelStorei(GLenum pname, GLint param)
+{
+ switch (pname) {
+ case GL_UNPACK_ALIGNMENT: currentContextData()->unpackAlignment = param; break;
+ }
+
+ auto event = currentContext()->createEvent(QStringLiteral("pixelStorei"));
+ if (!event)
+ return;
+ event->addInt(pname);
+ event->addInt(param);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glPolygonOffset(GLfloat factor, GLfloat units)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("polygonOffset"));
+ if (!event)
+ return;
+ event->addFloat(factor);
+ event->addFloat(units);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
+ GLenum type, void * pixels)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("readPixels"), true);
+ if (!event)
+ return;
+ const auto id = event->id();
+ event->addInt(x);
+ event->addInt(y);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(format);
+ event->addInt(type);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ const auto value = queryValue(id).toByteArray();
+ std::memcpy(pixels, value.constData(), value.size());
+}
+
+static void glReleaseShaderCompiler()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("releaseShaderCompiler"));
+ if (!event)
+ return;
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width,
+ GLsizei height)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("renderbufferStorage"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(internalformat);
+ event->addInt(width);
+ event->addInt(height);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glSampleCoverage(GLfloat value, GLboolean invert)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("sampleCoverage"));
+ if (!event)
+ return;
+ event->addFloat(value);
+ event->addInt(invert);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glScissor(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("scissor"));
+ if (!event)
+ return;
+ event->addInt(x);
+ event->addInt(y);
+ event->addInt(width);
+ event->addInt(height);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glShaderBinary(GLsizei count, const GLuint * shaders, GLenum binaryformat,
+ const void * binary, GLsizei length)
+{
+ Q_UNUSED(count);
+ Q_UNUSED(shaders);
+ Q_UNUSED(binaryformat);
+ Q_UNUSED(binary);
+ Q_UNUSED(length);
+ qFatal("WebGL does not allow precompiled shaders");
+}
+
+static void glShaderSource(GLuint shader, GLsizei count, const GLchar *const* string,
+ const GLint * length)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("shaderSource"));
+ if (!event)
+ return;
+ event->addUInt(shader);
+ event->addInt(count);
+ for (int i = 0; i < count; ++i) {
+ if (!length)
+ event->addString(QString::fromLatin1(string[i]));
+ else
+ event->addString(QString::fromLatin1(string[i], length[i]));
+ }
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilFunc(GLenum func, GLint ref, GLuint mask)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilFunc"));
+ if (!event)
+ return;
+ event->addInt(func);
+ event->addInt(ref);
+ event->addUInt(mask);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilFuncSeparate"));
+ if (!event)
+ return;
+ event->addInt(face);
+ event->addInt(func);
+ event->addInt(ref);
+ event->addUInt(mask);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilMask(GLuint mask)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilMask"));
+ if (!event)
+ return;
+ event->addUInt(mask);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilMaskSeparate(GLenum face, GLuint mask)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilMaskSeparate"));
+ if (!event)
+ return;
+ event->addInt(face);
+ event->addUInt(mask);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilOp"));
+ if (!event)
+ return;
+ event->addInt(fail);
+ event->addInt(zfail);
+ event->addInt(zpass);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("stencilOpSeparate"));
+ if (!event)
+ return;
+ event->addInt(face);
+ event->addInt(sfail);
+ event->addInt(dpfail);
+ event->addInt(dppass);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width,
+ GLsizei height, GLint border, GLenum format, GLenum type,
+ const void * pixels)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("texImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(internalformat);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(border);
+ event->addInt(format);
+ event->addInt(type);
+
+ if (pixels) {
+ const int len = imageSize(width, height, format, type, currentContextData()->pixelStorage);
+ event->addData(QByteArray((const char *)pixels, len));
+ } else {
+ event->addNull();
+ }
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glTexParameterf(GLenum target, GLenum pname, GLfloat param)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("texParameterf"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(pname);
+ event->addFloat(param);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glTexParameterfv(GLenum target, GLenum pname, const GLfloat * params)
+{
+ qFatal("glTexParameterfv not implemented");
+ Q_UNUSED(target);
+ Q_UNUSED(pname);
+ Q_UNUSED(params);
+}
+
+static void glTexParameteri(GLenum target, GLenum pname, GLint param)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("texParameteri"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(pname);
+ event->addInt(param);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glTexParameteriv(GLenum target, GLenum pname, const GLint * params)
+{
+ qFatal("glTexParameteriv not implemented");
+ Q_UNUSED(target);
+ Q_UNUSED(pname);
+ Q_UNUSED(params);
+}
+
+static void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height, GLenum format, GLenum type,
+ const void * pixels)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("texSubImage2D"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(level);
+ event->addInt(xoffset);
+ event->addInt(yoffset);
+ event->addInt(width);
+ event->addInt(height);
+ event->addInt(format);
+ event->addInt(type);
+ if (pixels) {
+ const int len = imageSize(width, height, format, type, currentContextData()->pixelStorage);
+ event->addData(QByteArray((const char *)pixels, len));
+ } else {
+ event->addNull();
+ }
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform1f(GLint location, GLfloat v0)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform1f"), false);
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addFloat(v0);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform1fv(GLint location, GLsizei count, const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform1fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform1i(GLint location, GLint v0)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform1i"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(v0);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform1iv(GLint location, GLsizei count, const GLint * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform1iv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < count; ++i)
+ event->addInt(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform2f(GLint location, GLfloat v0, GLfloat v1)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform2f"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addFloat(v0);
+ event->addFloat(v1);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform2fv(GLint location, GLsizei count, const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform2fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 2 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform2i(GLint location, GLint v0, GLint v1)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform2i"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(v0);
+ event->addInt(v1);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform2iv(GLint location, GLsizei count, const GLint * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform2iv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 2 * count; ++i)
+ event->addInt(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform3f"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addFloat(v0);
+ event->addFloat(v1);
+ event->addFloat(v2);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform3fv(GLint location, GLsizei count, const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform3fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 3 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform3i"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(v0);
+ event->addInt(v1);
+ event->addInt(v2);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform3iv(GLint location, GLsizei count, const GLint * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform3iv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 3 * count; ++i)
+ event->addInt(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform4f"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addFloat(v0);
+ event->addFloat(v1);
+ event->addFloat(v2);
+ event->addFloat(v3);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform4fv(GLint location, GLsizei count, const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform4fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 4 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform4i"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(v0);
+ event->addInt(v1);
+ event->addInt(v2);
+ event->addInt(v3);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniform4iv(GLint location, GLsizei count, const GLint * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniform4iv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ for (int i = 0; i < 4 * count; ++i)
+ event->addInt(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose,
+ const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniformMatrix2fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ event->addInt(transpose);
+ for (int i = 0; i < 4 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose,
+ const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniformMatrix3fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ event->addInt(transpose);
+ for (int i = 0; i < 9 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose,
+ const GLfloat * value)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("uniformMatrix4fv"));
+ if (!event)
+ return;
+ event->addInt(location);
+ event->addInt(count);
+ event->addInt(transpose);
+ for (int i = 0; i < 16 * count; ++i)
+ event->addFloat(value[i]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glUseProgram(GLuint program)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("useProgram"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ currentContextData()->currentProgram = program;
+}
+
+static void glValidateProgram(GLuint program)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("validateProgram"));
+ if (!event)
+ return;
+ event->addUInt(program);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib1f(GLuint index, GLfloat x)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib1f"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(x);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib1fv(GLuint index, const GLfloat * v)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib1fv"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(v[0]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib2f"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(x);
+ event->addFloat(y);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib2fv(GLuint index, const GLfloat * v)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib2fv"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(v[0]);
+ event->addFloat(v[1]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib3f"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(x);
+ event->addFloat(y);
+ event->addFloat(z);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib3fv(GLuint index, const GLfloat * v)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib3fv"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(v[0]);
+ event->addFloat(v[1]);
+ event->addFloat(v[2]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib4f"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(x);
+ event->addFloat(y);
+ event->addFloat(z);
+ event->addFloat(w);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttrib4fv(GLuint index, const GLfloat * v)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttrib4fv"));
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addFloat(v[0]);
+ event->addFloat(v[1]);
+ event->addFloat(v[2]);
+ event->addFloat(v[3]);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized,
+ GLsizei stride, const void * pointer)
+{
+ ContextData *d = currentContextData();
+ ContextData::VertexAttrib &va(d->vertexAttribPointers[index]);
+ va.arrayBufferBinding = d->boundArrayBuffer;
+ va.size = size;
+ va.type = type;
+ va.normalized = normalized;
+ va.stride = stride;
+ va.pointer = (void *) pointer;
+
+ if (d->boundArrayBuffer) {
+ auto event = currentContext()->createEvent(QStringLiteral("vertexAttribPointer"), false);
+ if (!event)
+ return;
+ event->addUInt(index);
+ event->addInt(size);
+ event->addInt(type);
+ event->addInt(normalized);
+ event->addInt(stride);
+ event->addUInt((quintptr) pointer);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ }
+}
+
+static void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("viewport"));
+ if (!event)
+ return;
+ event->addInt(x);
+ event->addInt(y);
+ event->addInt(width);
+ event->addInt(height);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+
+ auto it = currentContextData()->cachedParameters.find(GL_VIEWPORT);
+ if (it != currentContextData()->cachedParameters.end())
+ it->setValue(QVariantList{ x, y, width, height });
+}
+
+static void glBlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+ GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+ GLbitfield mask, GLenum filter)
+{
+ auto event = currentContext()->createEvent(QStringLiteral("blitFramebufferEXT"));
+ if (!event)
+ return;
+ event->addInt(srcX0);
+ event->addInt(srcY0);
+ event->addInt(srcX1);
+ event->addInt(srcY1);
+ event->addInt(dstX0);
+ event->addInt(dstY0);
+ event->addInt(dstX1);
+ event->addInt(dstY1);
+ event->addUInt(mask);
+ event->addInt(filter);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glRenderbufferStorageMultisampleEXT(GLenum target, GLsizei samples,
+ GLenum internalformat, GLsizei width,
+ GLsizei height)
+{
+ auto event = currentContext()->createEvent(
+ QStringLiteral("renderbufferStorageMultisampleEXT"));
+ if (!event)
+ return;
+ event->addInt(target);
+ event->addInt(samples);
+ event->addInt(internalformat);
+ event->addInt(width);
+ event->addInt(height);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+static void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint *params)
+{
+ qFatal("glGetTexLevelParameteriv not supported");
+ Q_UNUSED(target);
+ Q_UNUSED(level);
+ Q_UNUSED(pname);
+ Q_UNUSED(params);
+}
+
+}
+
+class QWebGLContextPrivate
+{
+public:
+ int id = -1;
+ static QAtomicInt nextId;
+ static QSet<int> waitingIds;
+ QPlatformSurface *currentSurface = nullptr;
+ QSurfaceFormat surfaceFormat;
+};
+
+QAtomicInt QWebGLContextPrivate::nextId(1);
+QSet<int> QWebGLContextPrivate::waitingIds;
+
+QWebGLContext::QWebGLContext(const QSurfaceFormat &format) :
+ d_ptr(new QWebGLContextPrivate)
+{
+ Q_D(QWebGLContext);
+ d->id = d->nextId.fetchAndAddOrdered(1);
+
+ qCDebug(lc, "Creating context %d", d->id);
+ d->surfaceFormat = format;
+ d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+}
+
+QWebGLContext::~QWebGLContext()
+{}
+
+QSurfaceFormat QWebGLContext::format() const
+{
+ Q_D(const QWebGLContext);
+ return d->surfaceFormat;
+}
+
+void QWebGLContext::swapBuffers(QPlatformSurface *surface)
+{
+ Q_UNUSED(surface);
+ auto event = currentContext()->createEvent(QStringLiteral("swapBuffers"), true);
+ if (!event)
+ return;
+ lockMutex();
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ waitCondition(1000);
+ unlockMutex();
+}
+
+bool QWebGLContext::makeCurrent(QPlatformSurface *surface)
+{
+ Q_D(QWebGLContext);
+
+ qCDebug(lc, "%p", surface);
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ const auto window = static_cast<QWebGLWindow *>(surface);
+ if (window->winId() == WId(-1))
+ return false;
+ }
+
+ auto context = QOpenGLContext::currentContext();
+ Q_ASSERT(context);
+ auto handle = static_cast<QWebGLContext *>(context->handle());
+ handle->d_func()->currentSurface = surface;
+ auto event = currentContext()->createEvent(QStringLiteral("makeCurrent"));
+ if (!event)
+ return false;
+ event->addInt(d->id);
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ auto window = static_cast<QWebGLWindow *>(surface);
+ if (s_contextData[handle->id()].cachedParameters.isEmpty()) {
+ auto future = window->d_func()->defaults.get_future();
+ std::future_status status = std::future_status::timeout;
+ while (status == std::future_status::timeout) {
+ if (!QWebGLIntegrationPrivate::instance()->findClientData(surface))
+ return false;
+ status = future.wait_for(std::chrono::milliseconds(100));
+ }
+ s_contextData[handle->id()].cachedParameters = future.get();
+ }
+ event->addInt(window->window()->width());
+ event->addInt(window->window()->height());
+ event->addInt(window->winId());
+ } else if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
+ qCDebug(lc, "QWebGLContext::makeCurrent: QSurface::Offscreen not implemented");
+ }
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+ return true;
+}
+
+void QWebGLContext::doneCurrent()
+{
+ auto event = currentContext()->createEvent(QStringLiteral("makeCurrent"));
+ if (!event)
+ return;
+ event->addInt(0);
+ event->addInt(0);
+ event->addInt(0);
+ event->addInt(0);
+ QCoreApplication::postEvent(QWebGLIntegrationPrivate::instance()->webSocketServer, event);
+}
+
+bool QWebGLContext::isValid() const
+{
+ Q_D(const QWebGLContext);
+ return d->id != -1;
+}
+
+QFunctionPointer QWebGLContext::getProcAddress(const char *procName)
+{
+ using namespace QWebGL;
+
+ struct FuncTab {
+ const char *name;
+ QFunctionPointer func;
+ } funcTab[] = {
+#define QWEBGLCONTEXT_ADD_FUNCTION(NAME) { #NAME, (QFunctionPointer) QWebGL::NAME }
+ QWEBGLCONTEXT_ADD_FUNCTION(glActiveTexture),
+ QWEBGLCONTEXT_ADD_FUNCTION(glAttachShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBindAttribLocation),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBindBuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBindFramebuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBindRenderbuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBindTexture),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlendColor),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlendEquation),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlendEquationSeparate),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlendFunc),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlendFuncSeparate),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBufferData),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBufferSubData),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCheckFramebufferStatus),
+ QWEBGLCONTEXT_ADD_FUNCTION(glClear),
+ QWEBGLCONTEXT_ADD_FUNCTION(glClearColor),
+ QWEBGLCONTEXT_ADD_FUNCTION(glClearDepthf),
+ QWEBGLCONTEXT_ADD_FUNCTION(glClearStencil),
+ QWEBGLCONTEXT_ADD_FUNCTION(glColorMask),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCompileShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCompressedTexImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCompressedTexSubImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCopyTexImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCopyTexSubImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCreateProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCreateShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glCullFace),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteBuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteFramebuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteRenderbuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDeleteTextures),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDepthFunc),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDepthMask),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDepthRangef),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDetachShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDisable),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDisableVertexAttribArray),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDrawArrays),
+ QWEBGLCONTEXT_ADD_FUNCTION(glDrawElements),
+ QWEBGLCONTEXT_ADD_FUNCTION(glEnable),
+ QWEBGLCONTEXT_ADD_FUNCTION(glEnableVertexAttribArray),
+ QWEBGLCONTEXT_ADD_FUNCTION(glFinish),
+ QWEBGLCONTEXT_ADD_FUNCTION(glFlush),
+ QWEBGLCONTEXT_ADD_FUNCTION(glFramebufferRenderbuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glFramebufferTexture2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glFrontFace),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGenBuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGenFramebuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGenRenderbuffers),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGenTextures),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGenerateMipmap),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetActiveAttrib),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetActiveUniform),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetAttachedShaders),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetAttribLocation),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetBooleanv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetBufferParameteriv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetError),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetFloatv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetFramebufferAttachmentParameteriv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetIntegerv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetProgramInfoLog),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetProgramiv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetRenderbufferParameteriv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetShaderInfoLog),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetShaderPrecisionFormat),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetShaderSource),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetShaderiv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetString),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetTexParameterfv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetTexParameteriv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetUniformLocation),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetUniformfv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetUniformiv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetVertexAttribPointerv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetVertexAttribfv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetVertexAttribiv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glHint),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsBuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsEnabled),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsFramebuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsRenderbuffer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsShader),
+ QWEBGLCONTEXT_ADD_FUNCTION(glIsTexture),
+ QWEBGLCONTEXT_ADD_FUNCTION(glLineWidth),
+ QWEBGLCONTEXT_ADD_FUNCTION(glLinkProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glPixelStorei),
+ QWEBGLCONTEXT_ADD_FUNCTION(glPolygonOffset),
+ QWEBGLCONTEXT_ADD_FUNCTION(glReadPixels),
+ QWEBGLCONTEXT_ADD_FUNCTION(glReleaseShaderCompiler),
+ QWEBGLCONTEXT_ADD_FUNCTION(glRenderbufferStorage),
+ QWEBGLCONTEXT_ADD_FUNCTION(glSampleCoverage),
+ QWEBGLCONTEXT_ADD_FUNCTION(glScissor),
+ QWEBGLCONTEXT_ADD_FUNCTION(glShaderBinary),
+ QWEBGLCONTEXT_ADD_FUNCTION(glShaderSource),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilFunc),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilFuncSeparate),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilMask),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilMaskSeparate),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilOp),
+ QWEBGLCONTEXT_ADD_FUNCTION(glStencilOpSeparate),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexParameterf),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexParameterfv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexParameteri),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexParameteriv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glTexSubImage2D),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform1f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform1fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform1i),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform1iv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform2f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform2fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform2i),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform2iv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform3f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform3fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform3i),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform3iv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform4f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform4fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform4i),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniform4iv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniformMatrix2fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniformMatrix3fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUniformMatrix4fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glUseProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glValidateProgram),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib1f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib1fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib2f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib2fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib3f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib3fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib4f),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttrib4fv),
+ QWEBGLCONTEXT_ADD_FUNCTION(glVertexAttribPointer),
+ QWEBGLCONTEXT_ADD_FUNCTION(glViewport),
+ QWEBGLCONTEXT_ADD_FUNCTION(glBlitFramebufferEXT),
+ QWEBGLCONTEXT_ADD_FUNCTION(glRenderbufferStorageMultisampleEXT),
+ QWEBGLCONTEXT_ADD_FUNCTION(glGetTexLevelParameteriv)
+#undef QWEBGLCONTEXT_ADD_FUNCTION
+ };
+
+
+ auto size = sizeof(funcTab) / sizeof(FuncTab);
+ for (auto i = 0u; i < size; ++i)
+ if (strcmp(procName, funcTab[i].name) == 0)
+ return funcTab[i].func;
+ return nullptr;
+}
+
+int QWebGLContext::id() const
+{
+ Q_D(const QWebGLContext);
+ return d->id;
+}
+
+QPlatformSurface *QWebGLContext::currentSurface() const
+{
+ Q_D(const QWebGLContext);
+ return d->currentSurface;
+}
+
+QWebGLFunctionCall *QWebGLContext::createEvent(const QString &functionName, bool wait)
+{
+ auto context = QOpenGLContext::currentContext();
+ Q_ASSERT(context);
+ const auto handle = static_cast<QWebGLContext *>(context->handle());
+ auto integrationPrivate = QWebGLIntegrationPrivate::instance();
+ const auto clientData = integrationPrivate->findClientData(handle->currentSurface());
+ if (!clientData || !clientData->socket
+ || clientData->socket->state() != QAbstractSocket::ConnectedState)
+ return nullptr;
+ const auto pointer = new QWebGLFunctionCall(functionName, handle->currentSurface(), wait);
+ if (pointer) {
+ if (wait)
+ QWebGLContextPrivate::waitingIds.insert(pointer->id());
+ } else {
+ qCWarning(lc, "Cannot create the event, the client is disconnected");
+ }
+
+ return pointer;
+}
+
+QVariant QWebGLContext::queryValue(int id)
+{
+ if (!QWebGLContextPrivate::waitingIds.contains(id)) {
+ qCWarning(lc, "Unexpected id (%d)", id);
+ return QVariant();
+ }
+
+ static auto queryValue = [](int id)
+ {
+ lockMutex();
+ waitCondition(10);
+ unlockMutex();
+ return QWebGLIntegrationPrivate::instance()->webSocketServer->queryValue(id);
+ };
+
+ const auto handle = static_cast<QWebGLContext *>(currentContext()->context()->handle());
+ QVariant variant = queryValue(id);
+ while (variant.isNull()) {
+ auto integrationPrivate = QWebGLIntegrationPrivate::instance();
+ const auto clientData = integrationPrivate->findClientData(handle->currentSurface());
+ if (!clientData || !clientData->socket
+ || clientData->socket->state() != QAbstractSocket::ConnectedState)
+ return QVariant();
+ variant = queryValue(id);
+ }
+ QWebGLContextPrivate::waitingIds.remove(id);
+ return variant;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglcontext.h b/src/plugins/platforms/webgl/qwebglcontext.h
new file mode 100644
index 0000000..7f7b7d7
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglcontext.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLCONTEXT_H
+#define QWEBGLCONTEXT_H
+
+#include <QtGui/qpa/qplatformopenglcontext.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLFunctionCall;
+class QWebGLContextPrivate;
+
+class QWebGLContext : public QPlatformOpenGLContext
+{
+public:
+ QWebGLContext(const QSurfaceFormat &format);
+ ~QWebGLContext() override;
+
+ QSurfaceFormat format() const override;
+
+ void swapBuffers(QPlatformSurface *surface) override;
+
+ bool makeCurrent(QPlatformSurface *surface) override;
+ void doneCurrent() override;
+
+ bool isValid() const override;
+
+ QFunctionPointer getProcAddress(const char *procName) override;
+
+ int id() const;
+ QPlatformSurface *currentSurface() const;
+
+ static QWebGLFunctionCall *createEvent(const QString &functionName, bool wait = false);
+ static QVariant queryValue(int id);
+
+private:
+ Q_DISABLE_COPY(QWebGLContext)
+ Q_DECLARE_PRIVATE(QWebGLContext)
+ QScopedPointer<QWebGLContextPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLCONTEXT_H
diff --git a/src/plugins/platforms/webgl/qwebglfunctioncall.cpp b/src/plugins/platforms/webgl/qwebglfunctioncall.cpp
new file mode 100644
index 0000000..8e469ec
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglfunctioncall.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglfunctioncall.h"
+
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonvalue.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qthread.h>
+#include <QtGui/qpa/qplatformsurface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLFunctionCallPrivate
+{
+public:
+ QString functionName;
+ QPlatformSurface *surface = nullptr;
+ QVariantList parameters;
+ bool wait = false;
+ int id = -1;
+ QThread *thread = nullptr;
+ static QAtomicInt nextId;
+ static int type;
+};
+
+QAtomicInt QWebGLFunctionCallPrivate::nextId(1);
+int QWebGLFunctionCallPrivate::type(QEvent::registerEventType());
+
+QWebGLFunctionCall::QWebGLFunctionCall(const QString &functionName,
+ QPlatformSurface *surface,
+ bool wait) :
+ QEvent(type()),
+ d_ptr(new QWebGLFunctionCallPrivate)
+{
+ Q_D(QWebGLFunctionCall);
+ d->functionName = functionName;
+ d->surface = surface;
+ d->wait = wait;
+ d->id = QWebGLFunctionCallPrivate::nextId.fetchAndAddOrdered(1);
+ d->thread = QThread::currentThread();
+}
+
+QWebGLFunctionCall::~QWebGLFunctionCall()
+{}
+
+QEvent::Type QWebGLFunctionCall::type()
+{
+ return Type(QWebGLFunctionCallPrivate::type);
+}
+
+int QWebGLFunctionCall::id() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->id;
+}
+
+QThread *QWebGLFunctionCall::thread() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->thread;
+}
+
+bool QWebGLFunctionCall::isBlocking() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->wait;
+}
+
+QPlatformSurface *QWebGLFunctionCall::surface() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->surface;
+}
+
+QString QWebGLFunctionCall::functionName() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->functionName;
+}
+
+void QWebGLFunctionCall::addString(const QString &value)
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(value);
+}
+
+void QWebGLFunctionCall::addInt(int value)
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(value);
+}
+
+void QWebGLFunctionCall::addUInt(uint value)
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(value);
+}
+
+void QWebGLFunctionCall::addFloat(float value)
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(static_cast<double>(value));
+}
+
+void QWebGLFunctionCall::addData(const QByteArray &data)
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(data);
+}
+
+void QWebGLFunctionCall::addNull()
+{
+ Q_D(QWebGLFunctionCall);
+ d->parameters.append(QVariant());
+}
+
+QVariantList QWebGLFunctionCall::parameters() const
+{
+ Q_D(const QWebGLFunctionCall);
+ return d->parameters;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglfunctioncall.h b/src/plugins/platforms/webgl/qwebglfunctioncall.h
new file mode 100644
index 0000000..947f583
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglfunctioncall.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLFUNCTIONCALL_H
+#define QWEBGLFUNCTIONCALL_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QPlatformSurface;
+class QString;
+class QThread;
+class QWebGLFunctionCallPrivate;
+
+class QWebGLFunctionCall : public QEvent
+{
+public:
+ QWebGLFunctionCall(const QString &functionName, QPlatformSurface *surface, bool wait = false);
+ ~QWebGLFunctionCall() override;
+
+ static Type type();
+
+ int id() const;
+ QThread *thread() const;
+ bool isBlocking() const;
+ QPlatformSurface *surface() const;
+
+ QString functionName() const;
+
+ void addString(const QString &value);
+ void addInt(int value);
+ void addUInt(uint value);
+ void addFloat(float value);
+ void addData(const QByteArray &data);
+ void addNull();
+
+ QVariantList parameters() const;
+
+private:
+ Q_DISABLE_COPY(QWebGLFunctionCall)
+ Q_DECLARE_PRIVATE(QWebGLFunctionCall)
+ QScopedPointer<QWebGLFunctionCallPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLFUNCTIONCALL_H
diff --git a/src/plugins/platforms/webgl/qwebglhttpserver.cpp b/src/plugins/platforms/webgl/qwebglhttpserver.cpp
new file mode 100644
index 0000000..e9945a2
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglhttpserver.cpp
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglhttpserver.h"
+
+#include "qwebglintegration.h"
+#include "qwebglwebsocketserver.h"
+
+#include <QtCore/qbuffer.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qurlquery.h>
+#include <QtGui/qclipboard.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qguiapplication.h>
+#include <QtNetwork/qnetworkinterface.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+
+#include <cctype>
+#include <cstdlib>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.httpserver")
+
+struct HttpRequest {
+ quint16 port = 0;
+
+ bool readMethod(QTcpSocket *socket);
+ bool readUrl(QTcpSocket *socket);
+ bool readStatus(QTcpSocket *socket);
+ bool readHeader(QTcpSocket *socket);
+
+ enum class State {
+ ReadingMethod,
+ ReadingUrl,
+ ReadingStatus,
+ ReadingHeader,
+ ReadingBody,
+ AllDone
+ } state = State::ReadingMethod;
+ QByteArray fragment;
+
+ enum class Method {
+ Unknown,
+ Head,
+ Get,
+ Put,
+ Post,
+ Delete,
+ } method = Method::Unknown;
+ quint32 byteSize = 0;
+ QUrl url;
+ QPair<quint8, quint8> version;
+ QMap<QByteArray, QByteArray> headers;
+};
+
+class QWebGLHttpServerPrivate
+{
+public:
+ QMap<QTcpSocket *, HttpRequest> clients;
+ QMap<QString, QPointer<QIODevice>> customRequestDevices;
+ QTcpServer server;
+ QPointer<QWebGLWebSocketServer> webSocketServer;
+};
+
+QWebGLHttpServer::QWebGLHttpServer(QWebGLWebSocketServer *webSocketServer, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QWebGLHttpServerPrivate)
+{
+ Q_D(QWebGLHttpServer);
+ d->webSocketServer = webSocketServer;
+
+ connect(&d->server, &QTcpServer::newConnection, this, &QWebGLHttpServer::clientConnected);
+}
+
+QWebGLHttpServer::~QWebGLHttpServer()
+{}
+
+bool QWebGLHttpServer::listen(const QHostAddress &address, quint16 port)
+{
+ Q_D(QWebGLHttpServer);
+ const auto ok = d->server.listen(address, port);
+ qCDebug(lc, "Listening in port %d", port);
+ return ok;
+}
+
+bool QWebGLHttpServer::isListening() const
+{
+ Q_D(const QWebGLHttpServer);
+ return d->server.isListening();
+}
+
+quint16 QWebGLHttpServer::serverPort() const
+{
+ Q_D(const QWebGLHttpServer);
+ return d->server.serverPort();
+}
+
+QIODevice *QWebGLHttpServer::customRequestDevice(const QString &name)
+{
+ Q_D(const QWebGLHttpServer);
+ return d->customRequestDevices.value(name, nullptr).data();
+}
+
+void QWebGLHttpServer::setCustomRequestDevice(const QString &name, QIODevice *device)
+{
+ Q_D(QWebGLHttpServer);
+ if (d->customRequestDevices.value(name))
+ d->customRequestDevices[name]->deleteLater();
+ d->customRequestDevices.insert(name, device);
+}
+
+void QWebGLHttpServer::clientConnected()
+{
+ Q_D(QWebGLHttpServer);
+ auto socket = d->server.nextPendingConnection();
+ connect(socket, &QTcpSocket::disconnected, this, &QWebGLHttpServer::clientDisconnected);
+ connect(socket, &QTcpSocket::readyRead, this, &QWebGLHttpServer::readData);
+}
+
+void QWebGLHttpServer::clientDisconnected()
+{
+ Q_D(QWebGLHttpServer);
+ auto socket = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(socket);
+ d->clients.remove(socket);
+ socket->deleteLater();
+}
+
+void QWebGLHttpServer::readData()
+{
+ Q_D(QWebGLHttpServer);
+ auto socket = qobject_cast<QTcpSocket *>(sender());
+ if (!d->clients.contains(socket))
+ d->clients[socket].port = d->server.serverPort();
+
+ auto request = &d->clients[socket];
+ bool error = false;
+
+ request->byteSize += socket->bytesAvailable();
+ if (Q_UNLIKELY(request->byteSize > 2048)) {
+ socket->write(QByteArrayLiteral("HTTP 413 – Request entity too large\r\n"));
+ socket->disconnectFromHost();
+ d->clients.remove(socket);
+ return;
+ }
+
+ if (Q_LIKELY(request->state == HttpRequest::State::ReadingMethod))
+ if (Q_UNLIKELY(error = !request->readMethod(socket)))
+ qCWarning(lc, "QWebGLHttpServer::readData: Invalid Method");
+
+ if (Q_LIKELY(!error && request->state == HttpRequest::State::ReadingUrl))
+ if (Q_UNLIKELY(error = !request->readUrl(socket)))
+ qCWarning(lc, "QWebGLHttpServer::readData: Invalid URL");
+
+ if (Q_LIKELY(!error && request->state == HttpRequest::State::ReadingStatus))
+ if (Q_UNLIKELY(error = !request->readStatus(socket)))
+ qCWarning(lc, "QWebGLHttpServer::readData: Invalid Status");
+
+ if (Q_LIKELY(!error && request->state == HttpRequest::State::ReadingHeader))
+ if (Q_UNLIKELY(error = !request->readHeader(socket)))
+ qCWarning(lc, "QWebGLHttpServer::readData: Invalid Header");
+
+ if (error) {
+ socket->disconnectFromHost();
+ d->clients.remove(socket);
+ } else if (!request->url.isEmpty()) {
+ Q_ASSERT(request->state != HttpRequest::State::ReadingUrl);
+ answerClient(socket, request->url);
+ d->clients.remove(socket);
+ }
+}
+
+void QWebGLHttpServer::answerClient(QTcpSocket *socket, const QUrl &url)
+{
+ Q_D(QWebGLHttpServer);
+ bool disconnect = true;
+ const auto path = url.path();
+
+ qCDebug(lc, "%s requested: %s",
+ qPrintable(socket->localAddress().toString()), qPrintable(path));
+
+ QByteArray answer = QByteArrayLiteral("HTTP/1.1 404 Not Found\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 136\r\n\r\n"
+ "<html>"
+ "<head><title>404 Not Found</title></head>"
+ "<body bgcolor=\"white\">"
+ "<center><h1>404 Not Found</h1></center>"
+ "</body>"
+ "</html>");
+ const auto addData = [&answer](const QByteArray &contentType, const QByteArray &data)
+ {
+ answer = QByteArrayLiteral("HTTP/1.0 200 OK \r\n");
+ QByteArray ret;
+ const auto dataSize = QString::number(data.size()).toUtf8();
+ answer += QByteArrayLiteral("Content-Type: ") + contentType + QByteArrayLiteral("\r\n") +
+ QByteArrayLiteral("Content-Length: ") + dataSize + QByteArrayLiteral("\r\n\r\n") +
+ data;
+ };
+
+ if (path == "/") {
+ QFile file(QStringLiteral(":/webgl/index.html"));
+ Q_ASSERT(file.exists());
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ Q_ASSERT(file.isOpen());
+ auto data = file.readAll();
+ addData(QByteArrayLiteral("text/html; charset=\"utf-8\""), data);
+ } else if (path == "/clipboard") {
+#ifndef QT_NO_CLIPBOARD
+ auto data = qGuiApp->clipboard()->text().toUtf8();
+ addData(QByteArrayLiteral("text/html; charset=\"utf-8\""), data);
+#else
+ qCWarning(lc, "Qt was built without clipboard support");
+#endif
+ } else if (path == "/webqt.js") {
+ QFile file(QStringLiteral(":/webgl/webqt.jsx"));
+ Q_ASSERT(file.exists());
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ Q_ASSERT(file.isOpen());
+ const auto host = url.host().toUtf8();
+ const auto port = QString::number(d->webSocketServer->port()).toUtf8();
+ QByteArray data = "var host = \"" + host + "\";\r\nvar port = " + port + ";\r\n";
+ data += file.readAll();
+ addData(QByteArrayLiteral("application/javascript"), data);
+ } else if (path == "/favicon.ico") {
+ QFile file(QStringLiteral(":/webgl/favicon.ico"));
+ Q_ASSERT(file.exists());
+ file.open(QIODevice::ReadOnly | QIODevice::Text);
+ Q_ASSERT(file.isOpen());
+ auto data = file.readAll();
+ addData(QByteArrayLiteral("image/x-icon"), data);
+ } else if (path == "/favicon.png") {
+ QBuffer buffer;
+ qGuiApp->windowIcon().pixmap(16, 16).save(&buffer, "png");
+ addData(QByteArrayLiteral("image/x-icon"), buffer.data());
+ } else if (auto device = d->customRequestDevices.value(path)) {
+ answer = QByteArrayLiteral("HTTP/1.0 200 OK \r\n"
+ "Content-Type: text/plain; charset=\"utf-8\"\r\n"
+ "Connection: Keep.Alive\r\n\r\n") +
+ device->readAll();
+ auto timer = new QTimer(device);
+ timer->setSingleShot(false);
+ connect(timer, &QTimer::timeout, [device, socket]()
+ {
+ if (device->bytesAvailable())
+ socket->write(device->readAll());
+ });
+ timer->start(1000);
+ disconnect = false;
+ }
+ socket->write(answer);
+ if (disconnect)
+ socket->disconnectFromHost();
+}
+
+bool HttpRequest::readMethod(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ const auto c = socket->read(1).at(0);
+ if (std::isupper(c) && fragment.size() < 6)
+ fragment += c;
+ else
+ finished = true;
+ }
+ if (finished) {
+ if (fragment == "HEAD")
+ method = Method::Head;
+ else if (fragment == "GET")
+ method = Method::Get;
+ else if (fragment == "PUT")
+ method = Method::Put;
+ else if (fragment == "POST")
+ method = Method::Post;
+ else if (fragment == "DELETE")
+ method = Method::Delete;
+ else
+ qCWarning(lc, "QWebGLHttpServer::HttpRequest::readMethod: Invalid operation %s",
+ fragment.data());
+
+ state = State::ReadingUrl;
+ fragment.clear();
+
+ return method != Method::Unknown;
+ }
+ return true;
+}
+
+bool HttpRequest::readUrl(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ char c;
+ if (!socket->getChar(&c))
+ return false;
+ if (std::isspace(c))
+ finished = true;
+ else
+ fragment += c;
+ }
+ if (finished) {
+ if (!fragment.startsWith("/")) {
+ qCWarning(lc, "QWebGLHttpServer::HttpRequest::readUrl: Invalid URL path %s",
+ fragment.constData());
+ return false;
+ }
+ url.setUrl(QStringLiteral("http://localhost:") + QString::number(port) +
+ QString::fromUtf8(fragment));
+ state = State::ReadingStatus;
+ if (!url.isValid()) {
+ qCWarning(lc, "QWebGLHttpServer::HttpRequest::readUrl: Invalid URL %s",
+ fragment.constData());
+ return false;
+ }
+ fragment.clear();
+ return true;
+ }
+ return true;
+}
+
+bool HttpRequest::readStatus(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ fragment += socket->read(1);
+ if (fragment.endsWith("\r\n")) {
+ finished = true;
+ fragment.chop(2);
+ }
+ }
+ if (finished) {
+ if (!std::isdigit(fragment.at(fragment.size() - 3)) ||
+ !std::isdigit(fragment.at(fragment.size() - 1))) {
+ qCWarning(lc, "QWebGLHttpServer::HttpRequest::::readStatus: Invalid version");
+ return false;
+ }
+ version = qMakePair(fragment.at(fragment.size() - 3) - '0',
+ fragment.at(fragment.size() - 1) - '0');
+ state = State::ReadingHeader;
+ fragment.clear();
+ }
+ return true;
+}
+
+bool HttpRequest::readHeader(QTcpSocket *socket)
+{
+ while (socket->bytesAvailable()) {
+ fragment += socket->read(1);
+ if (fragment.endsWith("\r\n")) {
+ if (fragment == "\r\n") {
+ state = State::ReadingBody;
+ fragment.clear();
+ return true;
+ } else {
+ fragment.chop(2);
+ const int index = fragment.indexOf(':');
+ if (index == -1)
+ return false;
+
+ const QByteArray key = fragment.mid(0, index).trimmed();
+ const QByteArray value = fragment.mid(index + 1).trimmed();
+ headers.insert(key, value);
+ if (QStringLiteral("host").compare(key, Qt::CaseInsensitive) == 0) {
+ auto parts = value.split(':');
+ if (parts.size() == 1) {
+ url.setHost(parts.first());
+ url.setPort(80);
+ } else {
+ url.setHost(parts.first());
+ url.setPort(std::strtoul(parts.at(1).constData(), nullptr, 10));
+ }
+ }
+ fragment.clear();
+ }
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglhttpserver.h b/src/plugins/platforms/webgl/qwebglhttpserver.h
new file mode 100644
index 0000000..b374831
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglhttpserver.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLHTTPSERVER_H
+#define QWEBGLHTTPSERVER_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtNetwork/qhostaddress.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+class QTcpSocket;
+class QString;
+class QUrl;
+class QWebGLWebSocketServer;
+class QWebGLHttpServerPrivate;
+
+class QWebGLHttpServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QWebGLHttpServer(QWebGLWebSocketServer *webSocketServer, QObject *parent = nullptr);
+ ~QWebGLHttpServer() override;
+
+ bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
+ bool isListening() const;
+ quint16 serverPort() const;
+
+ QIODevice *customRequestDevice(const QString &name);
+ void setCustomRequestDevice(const QString &name, QIODevice *device);
+
+private slots:
+ void clientConnected();
+ void clientDisconnected();
+ void readData();
+ void answerClient(QTcpSocket *socket, const QUrl &urls);
+
+private:
+ Q_DISABLE_COPY(QWebGLHttpServer)
+ Q_DECLARE_PRIVATE(QWebGLHttpServer)
+ QScopedPointer<QWebGLHttpServerPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLHTTPSERVER_H
diff --git a/src/plugins/platforms/webgl/qwebglintegration.cpp b/src/plugins/platforms/webgl/qwebglintegration.cpp
new file mode 100644
index 0000000..85ec2f1
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglintegration.cpp
@@ -0,0 +1,645 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglintegration.h"
+#include "qwebglintegration_p.h"
+
+#include "qwebglwindow.h"
+#include "qwebglcontext.h"
+#include "qwebglcontext.h"
+#include "qwebglhttpserver.h"
+#include "qwebglwebsocketserver.h"
+#include "qwebglplatformservices.h"
+
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qclipboard.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qsurfaceformat.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qoffscreensurface.h>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtPlatformCompositorSupport/private/qopenglcompositorbackingstore_p.h>
+#include <QtThemeSupport/private/qgenericunixthemes_p.h>
+#include <QtWebSockets/qwebsocket.h>
+
+#if defined(QT_QUICK_LIB)
+#include <QtQuick/qquickwindow.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcWebGL, "qt.qpa.webgl")
+
+QWebGLIntegrationPrivate *QWebGLIntegrationPrivate::instance()
+{
+ auto platformIntegration = QGuiApplicationPrivate::instance()->platformIntegration();
+ return static_cast<QWebGLIntegration *>(platformIntegration)->d_ptr.data();
+}
+
+QWebGLIntegration::QWebGLIntegration(quint16 port) :
+ d_ptr(new QWebGLIntegrationPrivate)
+{
+ Q_D(QWebGLIntegration);
+ d->q_ptr = this;
+ d->httpPort = port;
+ d->touchDevice = new QTouchDevice;
+ d->touchDevice->setName("EmulatedTouchDevice");
+ d->touchDevice->setType(QTouchDevice::TouchScreen);
+ d->touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Pressure |
+ QTouchDevice::MouseEmulation);
+ d->touchDevice->setMaximumTouchPoints(6);
+ QWindowSystemInterface::registerTouchDevice(d->touchDevice);
+
+ qCDebug(lcWebGL, "WebGL QPA Plugin created");
+ qRegisterMetaType<QWebSocket *>("QWebSocket *");
+ qRegisterMetaType<QWebGLWebSocketServer::MessageType>("QWebGLWebSocketServer::MessageType");
+}
+
+QWebGLIntegration::~QWebGLIntegration()
+{
+ Q_D(QWebGLIntegration);
+ QWindowSystemInterface::unregisterTouchDevice(d->touchDevice);
+}
+
+QWebGLIntegration *QWebGLIntegration::instance()
+{
+ return static_cast<QWebGLIntegration *>(qGuiApp->platformNativeInterface());
+}
+
+void QWebGLIntegration::initialize()
+{
+ Q_D(QWebGLIntegration);
+ d->inputContext = QPlatformInputContextFactory::create();
+ d->screen = new QWebGLScreen;
+ screenAdded(d->screen, true);
+
+ d->webSocketServer = new QWebGLWebSocketServer;
+ d->httpServer = new QWebGLHttpServer(d->webSocketServer, this);
+ bool ok = d->httpServer->listen(QHostAddress::Any, d->httpPort);
+ if (!ok)
+ qFatal("QWebGLIntegration::initialize: Failed to initialize");
+ d->webSocketServerThread = new QThread(this);
+ d->webSocketServerThread->setObjectName("WebSocketServer");
+ d->webSocketServer->moveToThread(d->webSocketServerThread);
+ connect(d->webSocketServerThread, &QThread::finished,
+ d->webSocketServer, &QObject::deleteLater);
+ QMetaObject::invokeMethod(d->webSocketServer, "create", Qt::QueuedConnection);
+ QMutexLocker lock(d->webSocketServer->mutex());
+ d->webSocketServerThread->start();
+ d->webSocketServer->waitCondition()->wait(d->webSocketServer->mutex());
+
+ qGuiApp->setQuitOnLastWindowClosed(false);
+}
+
+void QWebGLIntegration::destroy()
+{
+ Q_D(QWebGLIntegration);
+ foreach (QWindow *w, qGuiApp->topLevelWindows())
+ w->destroy();
+
+ destroyScreen(d->screen);
+
+ d->screen = nullptr;
+
+ d->webSocketServerThread->quit();
+ d->webSocketServerThread->wait();
+ delete d->webSocketServerThread;
+}
+
+QAbstractEventDispatcher *QWebGLIntegration::createEventDispatcher() const
+{
+#ifdef Q_OS_WIN
+ return new QWindowsGuiEventDispatcher;
+#else
+ return createUnixEventDispatcher();
+#endif // Q_OS_WIN
+}
+
+QPlatformServices *QWebGLIntegration::services() const
+{
+ Q_D(const QWebGLIntegration);
+ return &d->services;
+}
+
+QPlatformInputContext *QWebGLIntegration::inputContext() const
+{
+ Q_D(const QWebGLIntegration);
+ return d->inputContext;
+}
+
+QPlatformFontDatabase *QWebGLIntegration::fontDatabase() const
+{
+ Q_D(const QWebGLIntegration);
+ return &d->fontDatabase;
+}
+
+QPlatformTheme *QWebGLIntegration::createPlatformTheme(const QString &name) const
+{
+#ifdef Q_OS_WIN
+ return QPlatformIntegration::createPlatformTheme(name);
+#else
+ return QGenericUnixTheme::createUnixTheme(name);
+#endif // Q_OS_WIN
+}
+
+QPlatformBackingStore *QWebGLIntegration::createPlatformBackingStore(QWindow *window) const
+{
+ Q_UNUSED(window);
+ return nullptr;
+}
+
+QPlatformWindow *QWebGLIntegration::createPlatformWindow(QWindow *window) const
+{
+ Q_D(const QWebGLIntegration);
+ qCDebug(lcWebGL, "Creating platform window for: %p", window);
+
+#if defined(QT_QUICK_LIB)
+ if (window->inherits("QQuickWindow")) {
+ auto quickWindow = (QQuickWindow *)window;
+ quickWindow->setPersistentSceneGraph(false);
+ quickWindow->setPersistentOpenGLContext(false);
+ }
+#endif
+
+ d->windows.append(window);
+ QObject::connect(window, &QWindow::destroyed, [=] ()
+ {
+ d->windows.removeAll(window);
+ });
+
+ QWindowSystemInterface::flushWindowSystemEvents();
+
+ QWebGLWindow *platformWindow = nullptr;
+ QWebSocket *socket = nullptr;
+ WId winId = -1;
+ {
+ QMutexLocker locker(&d->clients.mutex);
+
+ if (d->clients.list.isEmpty()) {
+ QMetaObject::invokeMethod(window, "close", Qt::QueuedConnection);
+ return new QWebGLWindow(window);
+ }
+
+ auto client = &d->clients.list.first();
+ window->setScreen(client->platformScreen->screen());
+ client->platformWindows.append(new QWebGLWindow(window));
+ platformWindow = client->platformWindows.last();
+ socket = client->socket;
+ platformWindow->create();
+ platformWindow->requestActivateWindow();
+ winId = platformWindow->winId();
+ }
+
+ const QVariantMap values {
+ { "x", platformWindow->geometry().x() },
+ { "y", platformWindow->geometry().y() },
+ { "width", platformWindow->geometry().width() },
+ { "height", platformWindow->geometry().height() },
+ { "winId", winId },
+ { "title", qApp->applicationDisplayName() }
+ };
+ d->sendMessage(socket, QWebGLWebSocketServer::MessageType::CreateCanvas, values);
+
+ QObject::connect(window, &QWindow::windowTitleChanged, [=](const QString &title)
+ {
+ const QVariantMap values{{ "title", title }, { "winId", winId }};
+ d->sendMessage(socket, QWebGLWebSocketServer::MessageType::ChangeTitle, values);
+ });
+ qCDebug(lcWebGL, "Created platform window %p for: %p", platformWindow, window);
+ return platformWindow;
+}
+
+QPlatformOpenGLContext *QWebGLIntegration::createPlatformOpenGLContext(QOpenGLContext *context)
+ const
+{
+ qCDebug(lcWebGL, "%p", context);
+ QVariant nativeHandle = context->nativeHandle();
+
+ const QSurfaceFormat adjustedFormat = context->format();
+ QWebGLContext *ctx = new QWebGLContext(adjustedFormat);
+ context->setNativeHandle(nativeHandle);
+ return ctx;
+}
+
+bool QWebGLIntegration::hasCapability(QPlatformIntegration::Capability cap) const
+{
+ // We assume that devices will have more and not less capabilities
+ switch (cap) {
+ case ThreadedPixmaps: return true;
+ case OpenGL: return true;
+ case ThreadedOpenGL: return true;
+ case WindowManagement: return false;
+ case RasterGLSurface: return true;
+ default: return QPlatformIntegration::hasCapability(cap);
+ }
+}
+
+QPlatformNativeInterface *QWebGLIntegration::nativeInterface() const
+{
+ return const_cast<QWebGLIntegration *>(this);
+}
+
+void QWebGLIntegration::openUrl(const QUrl &url)
+{
+ Q_D(QWebGLIntegration);
+ qCDebug(lcWebGL, "%s", qPrintable(url.toString()));
+ QMutexLocker locker(&d->clients.mutex);
+ for (auto &clientData : d->clients.list) {
+ const QVariantMap values {
+ { "url", url }
+ };
+ d->sendMessage(clientData.socket, QWebGLWebSocketServer::MessageType::OpenUrl, values);
+ }
+}
+
+QWebGLIntegrationPrivate::ClientData *QWebGLIntegrationPrivate::findClientData(
+ const QWebSocket *socket)
+{
+ QMutexLocker locker(&clients.mutex);
+ auto it = std::find_if(clients.list.begin(), clients.list.end(), [=](const ClientData &data)
+ {
+ return data.socket == socket;
+ });
+
+ return it != clients.list.end() ? &*it : nullptr;
+}
+
+QWebGLIntegrationPrivate::ClientData *QWebGLIntegrationPrivate::findClientData(
+ const QPlatformSurface *surface)
+{
+ QMutexLocker locker(&clients.mutex);
+ auto it = std::find_if(clients.list.begin(), clients.list.end(), [=](const ClientData &data)
+ {
+ if (!data.platformWindows.isEmpty() && data.platformWindows.last()->surface())
+ return surface == data.platformWindows.last()->surface()->surfaceHandle();
+ return false;
+ });
+ return it != clients.list.end() ? &*it : nullptr;
+}
+
+QWebGLWindow *QWebGLIntegrationPrivate::findWindow(const ClientData &clientData, WId winId)
+{
+ auto &windows = clientData.platformWindows;
+ auto it = std::find_if(windows.begin(), windows.end(), [winId](QWebGLWindow *window)
+ {
+ return window->winId() == winId;
+ });
+ Q_ASSERT(it != windows.end());
+ return *it;
+}
+
+void QWebGLIntegrationPrivate::clientConnected(QWebSocket *socket,
+ const int width,
+ const int height,
+ const double physicalWidth,
+ const double physicalHeight)
+{
+ Q_Q(QWebGLIntegration);
+ qCDebug(lcWebGL, "%p, Size: %dx%d. Physical Size: %fx%f",
+ socket, width, height, physicalWidth, physicalHeight);
+ QWebGLIntegrationPrivate::ClientData client;
+ client.socket = socket;
+ client.platformScreen = new QWebGLScreen(QSize(width, height),
+ QSizeF(physicalWidth, physicalHeight));
+ clients.mutex.lock();
+ clients.list.append(client);
+ clients.mutex.unlock();
+ q->screenAdded(client.platformScreen, true);
+ connectNextClient();
+}
+
+void QWebGLIntegrationPrivate::clientDisconnected(QWebSocket *socket)
+{
+ qCDebug(lcWebGL, "%p", socket);
+ const auto predicate = [=](const QWebGLIntegrationPrivate::ClientData &item)
+ {
+ return socket == item.socket;
+ };
+
+ clients.mutex.lock();
+ auto it = std::find_if(clients.list.begin(), clients.list.end(), predicate);
+ if (it != clients.list.end()) {
+ for (auto platformWindow : it->platformWindows) {
+ auto window = platformWindow->window();
+ QTimer::singleShot(0, window, &QWindow::close);
+ }
+ clients.list.erase(it);
+ }
+ clients.mutex.unlock();
+ connectNextClient();
+}
+
+void QWebGLIntegrationPrivate::connectNextClient()
+{
+ static QMutex connecting;
+ if (connecting.tryLock()) {
+ QTimer::singleShot(1000, [=]() {
+ clients.mutex.lock();
+ if (!clients.list.isEmpty()) {
+ const auto clientData = clients.list.first();
+ qCDebug(lcWebGL, "Connecting first client in the queue (%p)",
+ clientData.socket);
+ for (auto window : windows)
+ QMetaObject::invokeMethod(window, "showFullScreen", Qt::QueuedConnection);
+ }
+ clients.mutex.unlock();
+ connecting.unlock();
+ });
+ };
+}
+
+void QWebGLIntegrationPrivate::sendMessage(QWebSocket *socket,
+ QWebGLWebSocketServer::MessageType type,
+ const QVariantMap &values) const
+{
+ const auto ok = QMetaObject::invokeMethod(webSocketServer, "sendMessage",
+ Q_ARG(QWebSocket*, socket),
+ Q_ARG(QWebGLWebSocketServer::MessageType, type),
+ Q_ARG(QVariantMap, values));
+#if defined(QT_DEBUG)
+ Q_ASSERT(ok);
+#else
+ Q_UNUSED(ok);
+#endif
+}
+
+void QWebGLIntegrationPrivate::onTextMessageReceived(QWebSocket *socket, const QString &message)
+{
+ QJsonParseError parseError;
+ const auto document = QJsonDocument::fromJson(message.toUtf8(), &parseError);
+ Q_ASSERT(parseError.error == QJsonParseError::NoError);
+ Q_ASSERT(document.isObject());
+ const auto object = document.object();
+ Q_ASSERT(object.contains("type"));
+ const auto type = object[QStringLiteral("type")].toString();
+
+ auto integrationPrivate = QWebGLIntegrationPrivate::instance();
+ const auto clientData = integrationPrivate->findClientData(socket);
+
+ if (type == QStringLiteral("connect"))
+ clientConnected(socket, object["width"].toInt(), object["height"].toInt(),
+ object["physicalWidth"].toDouble(), object["physicalHeight"].toDouble());
+ else if (!clientData || clientData->platformWindows.isEmpty())
+ qCWarning(lcWebGL, "Message received before connect %s", qPrintable(message));
+ else if (type == QStringLiteral("default_context_parameters"))
+ handleDefaultContextParameters(*clientData, object);
+ else if (type == QStringLiteral("gl_response"))
+ handleGlResponse(object);
+ else if (type == QStringLiteral("mouse"))
+ handleMouse(*clientData, object);
+ else if (type == QStringLiteral("wheel"))
+ handleWheel(*clientData, object);
+ else if (type == QStringLiteral("touch"))
+ handleTouch(*clientData, object);
+ else if (type.startsWith("key"))
+ handleKeyboard(*clientData, type, object);
+ else if (type == QStringLiteral("canvas_resize"))
+ handleCanvasResize(*clientData, object);
+}
+
+void QWebGLIntegrationPrivate::handleDefaultContextParameters(const ClientData &clientData,
+ const QJsonObject &object)
+{
+ const auto winId = object.value("name").toInt(-1);
+ Q_ASSERT(winId != -1);
+ QWebGLWindow *platformWindow = findWindow(clientData, winId);
+ Q_ASSERT(platformWindow);
+ auto data = object.toVariantMap();
+ data.remove("name");
+ data.remove("type");
+ QMap<GLenum, QVariant> result;
+ for (auto it = data.cbegin(), end = data.cend(); it != end; ++it)
+ result.insert(it.key().toInt(), *it);
+ platformWindow->setDefaults(result);
+}
+
+void QWebGLIntegrationPrivate::handleGlResponse(const QJsonObject &object)
+{
+ qCDebug(lcWebGL, ) << "gl_response message received" << object;
+ QMutexLocker locker(&waitMutex);
+ const auto id = object["id"];
+ const auto value = object["value"].toVariant();
+ Q_ASSERT(pendingResponses.contains(id.toInt()));
+ receivedResponses.insert(id.toInt(), value);
+ pendingResponses.removeOne(id.toInt());
+ waitCondition.wakeAll();
+}
+
+void QWebGLIntegrationPrivate::handleCanvasResize(const ClientData &clientData,
+ const QJsonObject &object)
+{
+ qCDebug(lcWebGL, ) << "canvas_resize message received" << object;
+ const auto width = object["width"].toInt();
+ const auto height = object["height"].toInt();
+ const auto physicalWidth = object["physicalWidth"].toDouble();
+ const auto physicalHeight = object["physicalHeight"].toDouble();
+ clientData.platformScreen->setGeometry(width, height, physicalWidth, physicalHeight);
+}
+
+void QWebGLIntegrationPrivate::handleMouse(const ClientData &clientData, const QJsonObject &object)
+{
+ const auto winId = object.value("name").toInt(-1);
+ Q_ASSERT(winId != -1);
+ QPointF localPos(object.value("layerX").toDouble(),
+ object.value("layerY").toDouble());
+ QPointF globalPos(object.value("clientX").toDouble(),
+ object.value("clientY").toDouble());
+ auto buttons = static_cast<Qt::MouseButtons>(object.value("buttons").toInt());
+ auto time = object.value("time").toDouble();
+ auto platformWindow = findWindow(clientData, winId);
+ QWindowSystemInterface::handleMouseEvent(platformWindow->window(),
+ static_cast<ulong>(time),
+ localPos,
+ globalPos,
+ Qt::MouseButtons(buttons),
+ Qt::NoModifier,
+ Qt::MouseEventNotSynthesized);
+}
+
+void QWebGLIntegrationPrivate::handleWheel(const ClientData &clientData, const QJsonObject &object)
+{
+ const auto winId = object.value("name").toInt(-1);
+ Q_ASSERT(winId != -1);
+ auto platformWindow = findWindow(clientData, winId);
+ auto time = object.value("time").toDouble();
+ QPointF localPos(object.value("layerX").toDouble(),
+ object.value("layerY").toDouble());
+ QPointF globalPos(object.value("clientX").toDouble(),
+ object.value("clientY").toDouble());
+ const int deltaX = -object.value("deltaX").toInt(0);
+ const int deltaY = -object.value("deltaY").toInt(0);
+ auto orientation = deltaY != 0 ? Qt::Vertical : Qt::Horizontal;
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(),
+ time,
+ localPos,
+ globalPos,
+ orientation == Qt::Vertical ? deltaY : deltaX,
+ orientation);
+}
+
+void QWebGLIntegrationPrivate::handleTouch(const ClientData &clientData, const QJsonObject &object)
+{
+ const auto winId = object.value("name").toInt(-1);
+ Q_ASSERT(winId != -1);
+ auto window = findWindow(clientData, winId)->window();
+ const auto time = object.value("time").toDouble();
+ const auto eventType = object.value("event").toString();
+ const auto changedTouch = object.value("changedTouches").toArray().first().toObject();
+ const auto clientX = changedTouch.value("clientX").toDouble();
+ const auto clientY = changedTouch.value("clientY").toDouble();
+ QList<QWindowSystemInterface::TouchPoint> points;
+ for (auto changedTouch : object.value("changedTouches").toArray()) {
+ QWindowSystemInterface::TouchPoint point; // support more than one
+ const auto pageX = changedTouch.toObject().value("pageX").toDouble();
+ const auto pageY = changedTouch.toObject().value("pageY").toDouble();
+ const auto radiousX = changedTouch.toObject().value("radiousX").toDouble();
+ const auto radiousY = changedTouch.toObject().value("radiousY").toDouble();
+ point.id = changedTouch.toObject().value("identifier").toInt(0);
+ point.pressure = changedTouch.toObject().value("force").toDouble(1.);
+ point.area.setX(pageX - radiousX);
+ point.area.setY(pageY - radiousY);
+ point.area.setWidth(radiousX * 2);
+ point.area.setHeight(radiousY * 2);
+ point.normalPosition.setX(changedTouch.toObject().value("normalPositionX").toDouble());
+ point.normalPosition.setY(changedTouch.toObject().value("normalPositionY").toDouble());
+ if (eventType == QStringLiteral("touchstart")) {
+ point.state = Qt::TouchPointPressed;
+ } else if (eventType == QStringLiteral("touchend")) {
+ qCDebug(lcWebGL, ) << "end" << object;
+ point.state = Qt::TouchPointReleased;
+ } else if (eventType == QStringLiteral("touchcancel")) {
+ QWindowSystemInterface::handleTouchCancelEvent(window,
+ time,
+ touchDevice,
+ Qt::NoModifier);
+ return;
+ } else {
+ point.state = Qt::TouchPointMoved;
+ }
+ point.rawPositions = {{ clientX, clientY }};
+ points.append(point);
+ }
+
+ QWindowSystemInterface::handleTouchEvent(window,
+ time,
+ touchDevice,
+ points,
+ Qt::NoModifier);
+}
+
+void QWebGLIntegrationPrivate::handleKeyboard(const ClientData &clientData,
+ const QString &type,
+ const QJsonObject &object)
+{
+ const QHash<QString, Qt::Key> keyMap {
+ { "Alt", Qt::Key_Alt },
+ { "ArrowDown", Qt::Key_Down },
+ { "ArrowLeft", Qt::Key_Left },
+ { "ArrowRight", Qt::Key_Right },
+ { "ArrowUp", Qt::Key_Up },
+ { "Backspace", Qt::Key_Backspace },
+ { "Control", Qt::Key_Control },
+ { "Delete", Qt::Key_Delete },
+ { "End", Qt::Key_End },
+ { "Enter", Qt::Key_Enter },
+ { "F1", Qt::Key_F1 },
+ { "F2", Qt::Key_F2 },
+ { "F3", Qt::Key_F3 },
+ { "F4", Qt::Key_F4 },
+ { "F5", Qt::Key_F5 },
+ { "F6", Qt::Key_F6 },
+ { "F7", Qt::Key_F7 },
+ { "F8", Qt::Key_F8 },
+ { "F9", Qt::Key_F9 },
+ { "F10", Qt::Key_F10 },
+ { "F11", Qt::Key_F11 },
+ { "F12", Qt::Key_F12 },
+ { "Escape", Qt::Key_Escape },
+ { "Home", Qt::Key_Home },
+ { "Insert", Qt::Key_Insert },
+ { "Meta", Qt::Key_Meta },
+ { "PageDown", Qt::Key_PageDown },
+ { "PageUp", Qt::Key_PageUp },
+ { "Shift", Qt::Key_Shift },
+ { "Space", Qt::Key_Space },
+ { "AltGraph", Qt::Key_AltGr },
+ { "Tab", Qt::Key_Tab },
+ { "Unidentified", Qt::Key_F },
+ { "OS", Qt::Key_Super_L }
+ };
+ const auto timestamp = static_cast<ulong>(object.value("time").toDouble(-1));
+ const auto keyName = object.value("key").toString();
+ const auto specialKey = keyMap.find(keyName);
+ QEvent::Type eventType;
+ if (type == QStringLiteral("keydown"))
+ eventType = QEvent::KeyPress;
+ else if (type == QStringLiteral("keyup"))
+ eventType = QEvent::KeyRelease;
+ else
+ return;
+ QString string(object.value("key").toString());
+ int key = object.value("which").toInt(0);
+ if (specialKey != keyMap.end()) {
+ key = *specialKey;
+ string.clear();
+ }
+
+ const auto window = clientData.platformWindows.last()->window();
+ QWindowSystemInterface::handleKeyEvent(window,
+ timestamp,
+ eventType,
+ key,
+ convertKeyboardModifiers(object),
+ string);
+}
+
+Qt::KeyboardModifiers QWebGLIntegrationPrivate::convertKeyboardModifiers(const QJsonObject &object)
+{
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ if (object.value("ctrlKey").toBool())
+ modifiers |= Qt::ControlModifier;
+ if (object.value("shiftKey").toBool())
+ modifiers |= Qt::ShiftModifier;
+ if (object.value("altKey").toBool())
+ modifiers |= Qt::AltModifier;
+ if (object.value("metaKey").toBool())
+ modifiers |= Qt::MetaModifier;
+ return modifiers;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglintegration.h b/src/plugins/platforms/webgl/qwebglintegration.h
new file mode 100644
index 0000000..856ccde
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglintegration.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLINTEGRATION_H
+#define QWEBGLINTEGRATION_H
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPlatformSurface;
+class QWebGLIntegrationPrivate;
+
+Q_DECLARE_LOGGING_CATEGORY(lcWebGL)
+
+class QWebGLIntegration : public QPlatformIntegration, public QPlatformNativeInterface
+{
+public:
+ QWebGLIntegration(quint16 port);
+ ~QWebGLIntegration();
+
+ static QWebGLIntegration *instance();
+
+ void initialize() override;
+ void destroy() override;
+
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QPlatformFontDatabase *fontDatabase() const override;
+ QPlatformServices *services() const override;
+ QPlatformInputContext *inputContext() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
+
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+
+ QPlatformNativeInterface *nativeInterface() const override;
+
+ void openUrl(const QUrl &url);
+
+private:
+ Q_DISABLE_COPY(QWebGLIntegration)
+ Q_DECLARE_PRIVATE(QWebGLIntegration)
+ QScopedPointer<QWebGLIntegrationPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLINTEGRATION_H
diff --git a/src/plugins/platforms/webgl/qwebglintegration_p.h b/src/plugins/platforms/webgl/qwebglintegration_p.h
new file mode 100644
index 0000000..dffcd9b
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglintegration_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLINTEGRATION_P_H
+#define QWEBGLINTEGRATION_P_H
+
+#include "qwebglscreen.h"
+#include "qwebglhttpserver.h"
+#include "qwebglplatformservices.h"
+#include "qwebglwebsocketserver.h"
+
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
+
+#ifdef Q_OS_WIN
+#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h>
+#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
+#else
+#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
+#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
+#endif // Q_OS_WIN
+
+QT_BEGIN_NAMESPACE
+
+class QWebSocket;
+class QWebGLIntegration;
+
+class QWebGLIntegrationPrivate
+{
+ Q_DECLARE_PUBLIC(QWebGLIntegration)
+public:
+ QWebGLIntegration *q_ptr = nullptr;
+
+ struct ClientData
+ {
+ QVector<QWebGLWindow *> platformWindows;
+ QWebSocket *socket;
+ QWebGLScreen *platformScreen = nullptr;
+ };
+
+ mutable QPlatformInputContext *inputContext = nullptr;
+ quint16 httpPort = 0;
+#if defined(Q_OS_WIN)
+ mutable QWindowsFontDatabase fontDatabase;
+#else
+ mutable QGenericUnixFontDatabase fontDatabase;
+#endif
+ mutable QWebGLPlatformServices services;
+ QWebGLHttpServer *httpServer = nullptr;
+ QWebGLWebSocketServer *webSocketServer = nullptr;
+ QWebGLScreen *screen = nullptr;
+ QThread *webSocketServerThread = nullptr;
+ mutable struct {
+ QList<ClientData> list;
+ QMutex mutex;
+ } clients;
+ mutable QVector<QWindow *> windows;
+
+ QMutex waitMutex;
+ QWaitCondition waitCondition;
+ QVector<int> pendingResponses;
+ QHash<int, QVariant> receivedResponses;
+ QTouchDevice *touchDevice = nullptr;
+
+ ClientData *findClientData(const QWebSocket *socket);
+ ClientData *findClientData(const QPlatformSurface *surface);
+ QWebGLWindow *findWindow(const ClientData &clientData, WId winId);
+
+ void clientConnected(QWebSocket *socket,
+ const int width,
+ const int height,
+ const double physicalWidth,
+ const double physicalHeight);
+ void clientDisconnected(QWebSocket *socket);
+
+ void connectNextClient();
+
+ void sendMessage(QWebSocket *socket,
+ QWebGLWebSocketServer::MessageType type,
+ const QVariantMap &values) const;
+ void onTextMessageReceived(QWebSocket *socket, const QString &message);
+ void handleDefaultContextParameters(const ClientData &clientData, const QJsonObject &object);
+ void handleGlResponse(const QJsonObject &object);
+ void handleCanvasResize(const ClientData &clientData, const QJsonObject &object);
+ void handleMouse(const ClientData &clientData, const QJsonObject &object);
+ void handleWheel(const ClientData &clientData, const QJsonObject &object);
+ void handleTouch(const ClientData &clientData, const QJsonObject &object);
+ void handleKeyboard(const ClientData &clientData,
+ const QString &type,
+ const QJsonObject &object);
+
+ Qt::KeyboardModifiers convertKeyboardModifiers(const QJsonObject &object);
+
+ static QWebGLIntegrationPrivate *instance();
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLINTEGRATION_P_H
diff --git a/src/plugins/platforms/webgl/qwebglmain.cpp b/src/plugins/platforms/webgl/qwebglmain.cpp
new file mode 100644
index 0000000..4f7ede7
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglmain.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglintegration.h"
+
+#include <QtGui/qpa/qplatformintegrationplugin.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLIntegrationPlugin : public QPlatformIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "webgl.json")
+public:
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
+};
+
+QPlatformIntegration* QWebGLIntegrationPlugin::create(const QString& system,
+ const QStringList& paramList)
+{
+ quint16 port = 8080;
+ if (!paramList.isEmpty()) {
+ for (const QString &parameter : qAsConst(paramList)) {
+ const QStringList parts = parameter.split('=');
+ if (parts.first() == QStringLiteral("port") && parts.size() == 2) {
+ if (parts.size() != 2) {
+ qCCritical(lcWebGL, "QWebGLIntegrationPlugin::create: No port specified");
+ return nullptr;
+ }
+ bool ok;
+ port = parts.last().toUInt(&ok);
+ if (!ok) {
+ qCCritical(lcWebGL, "QWebGLIntegrationPlugin::create: Invalid port number");
+ return nullptr;
+ }
+ }
+ }
+ }
+ if (!system.compare(QLatin1String("webgl"), Qt::CaseInsensitive))
+ return new QWebGLIntegration(port);
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "qwebglmain.moc"
diff --git a/src/plugins/platforms/webgl/qwebglplatformservices.cpp b/src/plugins/platforms/webgl/qwebglplatformservices.cpp
new file mode 100644
index 0000000..3f8dfbc
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglplatformservices.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglplatformservices.h"
+
+#include "qwebglintegration.h"
+
+#include <QtGui/qguiapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QWebGLPlatformServices::openUrl(const QUrl &url)
+{
+ auto integration = (QWebGLIntegration*)qGuiApp->platformNativeInterface();
+ integration->openUrl(url);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglplatformservices.h b/src/plugins/platforms/webgl/qwebglplatformservices.h
new file mode 100644
index 0000000..40e1989
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglplatformservices.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLPLATFORMSERVICES_H
+#define QWEBGLPLATFORMSERVICES_H
+
+#include <QtGui/qpa/qplatformservices.h>
+
+QT_BEGIN_NAMESPACE
+
+class QUrl;
+
+class QWebGLPlatformServices : public QPlatformServices
+{
+public:
+ QWebGLPlatformServices() = default;
+
+ virtual bool openUrl(const QUrl &url) override;
+
+private:
+ Q_DISABLE_COPY(QWebGLPlatformServices)
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLPLATFORMSERVICES_H
diff --git a/src/plugins/platforms/webgl/qwebglscreen.cpp b/src/plugins/platforms/webgl/qwebglscreen.cpp
new file mode 100644
index 0000000..cb6a32e
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglscreen.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglscreen.h"
+
+#include "qwebglwindow.h"
+
+#include <QtCore/qtextstream.h>
+#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtGui/qpa/qplatformcursor.h>
+#include <QtGui/qwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLScreenPrivate
+{
+public:
+ QSize size = QSize(1920, 1080);
+ QSizeF physicalSize = QSizeF(531.3, 298.9);
+};
+
+QWebGLScreen::QWebGLScreen() :
+ d_ptr(new QWebGLScreenPrivate)
+{}
+
+QWebGLScreen::QWebGLScreen(const QSize size, const QSizeF physicalSize) :
+ QWebGLScreen()
+{
+ Q_D(QWebGLScreen);
+ d->size = size;
+ d->physicalSize = physicalSize;
+}
+
+QWebGLScreen::~QWebGLScreen()
+{}
+
+QRect QWebGLScreen::geometry() const
+{
+ Q_D(const QWebGLScreen);
+ return QRect(QPoint(0, 0), d->size);
+}
+
+int QWebGLScreen::depth() const
+{
+ return 32;
+}
+
+QImage::Format QWebGLScreen::format() const
+{
+ return QImage::Format_ARGB32;
+}
+
+QSizeF QWebGLScreen::physicalSize() const
+{
+ Q_D(const QWebGLScreen);
+ return d->physicalSize;
+}
+
+QDpi QWebGLScreen::logicalDpi() const
+{
+ return QPlatformScreen::logicalDpi();
+}
+
+qreal QWebGLScreen::pixelDensity() const
+{
+ return QPlatformScreen::pixelDensity();
+}
+
+qreal QWebGLScreen::refreshRate() const
+{
+ return 60;
+}
+
+void QWebGLScreen::setGeometry(int width, int height, const int physicalWidth,
+ const int physicalHeight)
+{
+ Q_D(QWebGLScreen);
+ d->size = QSize(width, height);
+ d->physicalSize = QSize(physicalWidth, physicalHeight);
+ resizeMaximizedWindows();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglscreen.h b/src/plugins/platforms/webgl/qwebglscreen.h
new file mode 100644
index 0000000..772af1a
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglscreen.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLSCREEN_H
+#define QWEBGLSCREEN_H
+
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpa/qplatformscreen.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLWindow;
+class QWebGLScreenPrivate;
+
+class QWebGLScreen : public QPlatformScreen
+{
+public:
+ QWebGLScreen();
+ QWebGLScreen(const QSize size, const QSizeF physicalSize);
+ ~QWebGLScreen();
+
+ QRect geometry() const override;
+ int depth() const override;
+ QImage::Format format() const override;
+
+ QSizeF physicalSize() const override;
+ QDpi logicalDpi() const override;
+ qreal pixelDensity() const override;
+
+ qreal refreshRate() const override;
+
+ void setGeometry(int width, int height, const int physicalWidth, const int physicalHeight);
+
+private:
+ friend class QWebGLWindow;
+
+ Q_DISABLE_COPY(QWebGLScreen)
+ Q_DECLARE_PRIVATE(QWebGLScreen)
+ QScopedPointer<QWebGLScreenPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLSCREEN_H
diff --git a/src/plugins/platforms/webgl/qwebglwebsocketserver.cpp b/src/plugins/platforms/webgl/qwebglwebsocketserver.cpp
new file mode 100644
index 0000000..6344d2d
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglwebsocketserver.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglwebsocketserver.h"
+
+#include "qwebglfunctioncall.h"
+#include "qwebglintegration.h"
+#include "qwebglintegration_p.h"
+#include "qwebglwindow.h"
+#include "qwebglwindow_p.h"
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtWebSockets/qwebsocket.h>
+#include <QtWebSockets/qwebsocketserver.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.websocketserver")
+
+const QHash<QString, Qt::Key> keyMap {
+ { "Alt", Qt::Key_Alt },
+ { "ArrowDown", Qt::Key_Down },
+ { "ArrowLeft", Qt::Key_Left },
+ { "ArrowRight", Qt::Key_Right },
+ { "ArrowUp", Qt::Key_Up },
+ { "Backspace", Qt::Key_Backspace },
+ { "Control", Qt::Key_Control },
+ { "Delete", Qt::Key_Delete },
+ { "End", Qt::Key_End },
+ { "Enter", Qt::Key_Enter },
+ { "F1", Qt::Key_F1 },
+ { "F2", Qt::Key_F2 },
+ { "F3", Qt::Key_F3 },
+ { "F4", Qt::Key_F4 },
+ { "F5", Qt::Key_F5 },
+ { "F6", Qt::Key_F6 },
+ { "F7", Qt::Key_F7 },
+ { "F8", Qt::Key_F8 },
+ { "F9", Qt::Key_F9 },
+ { "F10", Qt::Key_F10 },
+ { "F11", Qt::Key_F11 },
+ { "F12", Qt::Key_F12 },
+ { "Escape", Qt::Key_Escape },
+ { "Home", Qt::Key_Home },
+ { "Insert", Qt::Key_Insert },
+ { "Meta", Qt::Key_Meta },
+ { "PageDown", Qt::Key_PageDown },
+ { "PageUp", Qt::Key_PageUp },
+ { "Shift", Qt::Key_Shift },
+ { "Space", Qt::Key_Space },
+ { "AltGraph", Qt::Key_AltGr },
+ { "Tab", Qt::Key_Tab },
+ { "Unidentified", Qt::Key_F },
+ { "OS", Qt::Key_Super_L }
+};
+
+inline QWebGLIntegration *webGLIntegration()
+{
+#ifdef QT_DEBUG
+ auto nativeInterface = dynamic_cast<QWebGLIntegration *>(qGuiApp->platformNativeInterface());
+ Q_ASSERT(nativeInterface);
+#else
+ auto nativeInterface = static_cast<QWebGLIntegration *>(qGuiApp->platformNativeInterface());
+#endif // QT_DEBUG
+ return nativeInterface;
+}
+
+class QWebGLWebSocketServerPrivate
+{
+public:
+ QWebSocketServer *server = nullptr;
+};
+
+QWebGLWebSocketServer::QWebGLWebSocketServer(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QWebGLWebSocketServerPrivate)
+{}
+
+QWebGLWebSocketServer::~QWebGLWebSocketServer()
+{}
+
+quint16 QWebGLWebSocketServer::port() const
+{
+ Q_D(const QWebGLWebSocketServer);
+ return d->server->serverPort();
+}
+
+QMutex *QWebGLWebSocketServer::mutex()
+{
+ return &QWebGLIntegrationPrivate::instance()->waitMutex;
+}
+
+QWaitCondition *QWebGLWebSocketServer::waitCondition()
+{
+ return &QWebGLIntegrationPrivate::instance()->waitCondition;
+}
+
+QVariant QWebGLWebSocketServer::queryValue(int id)
+{
+ QMutexLocker locker(&QWebGLIntegrationPrivate::instance()->waitMutex);
+ if (QWebGLIntegrationPrivate::instance()->receivedResponses.contains(id))
+ return QWebGLIntegrationPrivate::instance()->receivedResponses.take(id);
+ return QVariant();
+}
+
+void QWebGLWebSocketServer::create()
+{
+ Q_D(QWebGLWebSocketServer);
+ d->server = new QWebSocketServer(QLatin1String("qtwebgl"), QWebSocketServer::NonSecureMode);
+ bool ok = d->server->listen(QHostAddress::Any);
+ if (ok)
+ connect(d->server, &QWebSocketServer::newConnection, this,
+ &QWebGLWebSocketServer::onNewConnection);
+
+ QMutexLocker lock(&QWebGLIntegrationPrivate::instance()->waitMutex);
+ QWebGLIntegrationPrivate::instance()->waitCondition.wakeAll();
+}
+
+void QWebGLWebSocketServer::sendMessage(QWebSocket *socket,
+ MessageType type,
+ const QVariantMap &values)
+{
+ if (!socket)
+ return;
+ QString typeString;
+ switch (type) {
+ case MessageType::Connect:
+ typeString = QStringLiteral("connect");
+ qCDebug(lc) << "Sending connect to " << socket << values;
+ break;
+ case MessageType::GlCommand: {
+ const quint32 id = values["id"].toUInt();
+ const auto functionName = values["function"].toString().toUtf8();
+ const auto parameters = values["parameters"].toList();
+ const quint32 parameterCount = parameters.size();
+ qCDebug(lc, "Sending gl_command %s (%d) to %p with %d parameters",
+ qPrintable(functionName), id, socket, parameterCount);
+ QByteArray data;
+ {
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << id << functionName << parameterCount;
+ for (const auto &value : qAsConst(parameters)) {
+ if (value.isNull()) {
+ stream << (quint8)'n';
+ } else switch (value.type()) {
+ case QVariant::Int:
+ stream << (quint8)'i' << value.toInt();
+ break;
+ case QVariant::UInt:
+ stream << (quint8)'u' << value.toUInt();
+ break;
+ case QVariant::Bool:
+ stream << (quint8)'b' << (quint8)value.toBool();
+ break;
+ case QVariant::Double:
+ stream << (quint8)'d' << value.toDouble();
+ break;
+ case QVariant::String:
+ stream << (quint8)'s' << value.toString().toUtf8();
+ break;
+ case QVariant::ByteArray: {
+ const auto byteArray = value.toByteArray();
+ if (data.isNull())
+ stream << (quint8)'n';
+ else
+ stream << (quint8)'x' << byteArray;
+ break;
+ }
+ default:
+ qCCritical(lc, "Unsupported type: %d", value.type());
+ break;
+ }
+ }
+ stream << (quint32)0xbaadf00d;
+ }
+ const quint32 totalMessageSize = data.size();
+ const quint32 maxMessageSize = 1024;
+ for (quint32 i = 0; i <= data.size() / maxMessageSize; ++i) {
+ const quint32 offset = i * maxMessageSize;
+ const quint32 size = qMin(totalMessageSize - offset, maxMessageSize);
+ const auto chunk = QByteArray::fromRawData(data.constData() + offset, size);
+ socket->sendBinaryMessage(chunk);
+ }
+ return;
+ }
+ case MessageType::CreateCanvas:
+ qCDebug(lc) << "Sending create_canvas to " << socket << values;
+ typeString = QStringLiteral("create_canvas");
+ break;
+ case MessageType::DestroyCanvas:
+ return; // TODO: In current implementation the canvas is not destroyed
+ qCDebug(lc) << "Sending destroy_canvas to " << socket << values;
+ typeString = QStringLiteral("destroy_canvas");
+ break;
+ case MessageType::OpenUrl:
+ qCDebug(lc) << "Sending open_url to " << socket << values;
+ typeString = QStringLiteral("open_url");
+ break;
+ case MessageType::ChangeTitle:
+ qCDebug(lc) << "Sending change_title to " << socket << values;
+ typeString = QStringLiteral("changle_title");
+ break;
+ }
+ QJsonDocument document;
+ auto commandObject = QJsonObject::fromVariantMap(values);
+ commandObject["type"] = typeString;
+ document.setObject(commandObject);
+ auto data = document.toJson(QJsonDocument::Compact);
+ socket->sendTextMessage(data);
+}
+
+bool QWebGLWebSocketServer::event(QEvent *event)
+{
+ int type = event->type();
+ if (type == QWebGLFunctionCall::type()) {
+ auto e = static_cast<QWebGLFunctionCall *>(event);
+ const QVariantMap values {
+ { "function", e->functionName() },
+ { "id", e->id() },
+ { "parameters", e->parameters() }
+ };
+ auto integrationPrivate = QWebGLIntegrationPrivate::instance();
+ auto clientData = integrationPrivate->findClientData(e->surface());
+ if (clientData && clientData->socket) {
+ sendMessage(clientData->socket, MessageType::GlCommand, values);
+ if (e->isBlocking())
+ integrationPrivate->pendingResponses.append(e->id());
+ return true;
+ }
+ return false;
+ }
+ return QObject::event(event);
+}
+
+void QWebGLWebSocketServer::onNewConnection()
+{
+ Q_D(QWebGLWebSocketServer);
+ QWebSocket *socket = d->server->nextPendingConnection();
+ if (socket) {
+ connect(socket, &QWebSocket::disconnected, this, &QWebGLWebSocketServer::onDisconnect);
+ connect(socket, &QWebSocket::textMessageReceived, this,
+ &QWebGLWebSocketServer::onTextMessageReceived);
+
+ const QVariantMap values{
+ { QStringLiteral("buildAbi"), QSysInfo::buildAbi() },
+ { QStringLiteral("buildCpuArchitecture"), QSysInfo::buildCpuArchitecture() },
+ { QStringLiteral("currentCpuArchitecture"), QSysInfo::currentCpuArchitecture() },
+ { QStringLiteral("kernelType"), QSysInfo::kernelType() },
+ { QStringLiteral("machineHostName"), QSysInfo::machineHostName() },
+ { QStringLiteral("prettyProductName"), QSysInfo::prettyProductName() },
+ { QStringLiteral("productType"), QSysInfo::productType() },
+ { QStringLiteral("productVersion"), QSysInfo::productVersion() }
+ };
+
+ sendMessage(socket, MessageType::Connect, values);
+ }
+}
+
+void QWebGLWebSocketServer::onDisconnect()
+{
+ QWebSocket *socket = qobject_cast<QWebSocket *>(sender());
+ Q_ASSERT(socket);
+ QWebGLIntegrationPrivate::instance()->clientDisconnected(socket);
+ socket->deleteLater();
+}
+
+void QWebGLWebSocketServer::onTextMessageReceived(const QString &message)
+{
+ const auto socket = qobject_cast<QWebSocket *>(sender());
+ QWebGLIntegrationPrivate::instance()->onTextMessageReceived(socket, message);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglwebsocketserver.h b/src/plugins/platforms/webgl/qwebglwebsocketserver.h
new file mode 100644
index 0000000..f2bbea0
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglwebsocketserver.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLWEBSOCKETSERVER_H
+#define QWEBGLWEBSOCKETSERVER_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMutex;
+class QWebSocket;
+class QWaitCondition;
+class QWebGLWebSocketServerPrivate;
+
+class QWebGLWebSocketServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum class MessageType
+ {
+ Connect,
+ GlCommand,
+ CreateCanvas,
+ DestroyCanvas,
+ OpenUrl,
+ ChangeTitle
+ };
+
+ QWebGLWebSocketServer(QObject *parent = nullptr);
+ ~QWebGLWebSocketServer() override;
+
+ quint16 port() const;
+
+ QMutex *mutex();
+ QWaitCondition *waitCondition();
+
+ QVariant queryValue(int id);
+
+public slots:
+ void create();
+ void sendMessage(QWebSocket *socket,
+ QWebGLWebSocketServer::MessageType type,
+ const QVariantMap &values);
+
+protected:
+ bool event(QEvent *event) override;
+
+private slots:
+ void onNewConnection();
+ void onDisconnect();
+ void onTextMessageReceived(const QString &message);
+
+private:
+ Q_DISABLE_COPY(QWebGLWebSocketServer)
+ Q_DECLARE_PRIVATE(QWebGLWebSocketServer)
+ QScopedPointer<QWebGLWebSocketServerPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLWEBSOCKETSERVER_H
diff --git a/src/plugins/platforms/webgl/qwebglwindow.cpp b/src/plugins/platforms/webgl/qwebglwindow.cpp
new file mode 100644
index 0000000..ff82c32
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglwindow.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebglwindow.h"
+#include "qwebglwindow_p.h"
+
+#include "qwebglintegration_p.h"
+#include "qwebglwebsocketserver.h"
+
+#include <QtCore/qtextstream.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qopenglcontext_p.h>
+#include <QtGui/qpa/qwindowsysteminterface.h>
+#include <QtGui/qpa/qplatformintegration.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtPlatformCompositorSupport/private/qopenglcompositorbackingstore_p.h>
+
+#include "qwebglwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.window")
+
+QAtomicInt QWebGLWindowPrivate::nextId(1);
+
+QWebGLWindowPrivate::QWebGLWindowPrivate(QWebGLWindow *p) :
+ q_ptr(p)
+{}
+
+QWebGLWindow::QWebGLWindow(QWindow *w) :
+ QPlatformWindow(w),
+ d_ptr(new QWebGLWindowPrivate(this))
+{
+ Q_D(QWebGLWindow);
+ d->raster = false;
+ d->flags = 0;
+}
+
+QWebGLWindow::~QWebGLWindow()
+{
+ destroy();
+}
+
+void QWebGLWindow::create()
+{
+ Q_D(QWebGLWindow);
+ if (d->flags.testFlag(QWebGLWindowPrivate::Created))
+ return;
+
+ d->id = QWebGLWindowPrivate::nextId.fetchAndAddAcquire(1);
+ qCDebug(lc, "Window %d created", d->id);
+
+ // Save the original surface type before changing to OpenGLSurface.
+ d->raster = (window()->surfaceType() == QSurface::RasterSurface);
+ if (d->raster) // change to OpenGL, but not for RasterGLSurface
+ window()->setSurfaceType(QSurface::OpenGLSurface);
+
+ if (window()->windowState() == Qt::WindowFullScreen) {
+ QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
+ QPlatformWindow::setGeometry(fullscreenRect);
+ QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
+ return;
+ }
+
+ d->flags = QWebGLWindowPrivate::Created;
+
+ if (window()->type() == Qt::Desktop)
+ return;
+
+ d->flags |= QWebGLWindowPrivate::HasNativeWindow;
+ setGeometry(window()->geometry()); // will become fullscreen
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size()));
+
+ if (d->raster) {
+ QOpenGLContext *context = new QOpenGLContext(QGuiApplication::instance());
+ context->setShareContext(qt_gl_global_share_context());
+ context->setFormat(d->format);
+ context->setScreen(window()->screen());
+ if (Q_UNLIKELY(!context->create()))
+ qFatal("QWebGL: Failed to create compositing context");
+ }
+}
+
+void QWebGLWindow::destroy()
+{
+ Q_D(QWebGLWindow);
+ qCDebug(lc, "Destroying %d", d->id);
+ if (d->flags.testFlag(QWebGLWindowPrivate::HasNativeWindow)) {
+ invalidateSurface();
+ }
+
+ d->flags = 0;
+
+ auto integrationPrivate = QWebGLIntegrationPrivate::instance();
+ auto clientData = integrationPrivate->findClientData(surface()->surfaceHandle());
+ if (clientData) {
+ const QVariantMap values {
+ { "winId", winId() }
+ };
+ if (clientData->socket)
+ integrationPrivate->sendMessage(clientData->socket,
+ QWebGLWebSocketServer::MessageType::DestroyCanvas,
+ values);
+ clientData->platformWindows.removeAll(this);
+ }
+}
+
+void QWebGLWindow::raise()
+{
+ QWindow *wnd = window();
+ if (wnd->type() != Qt::Desktop) {
+ QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
+ }
+}
+
+QSurfaceFormat QWebGLWindow::format() const
+{
+ Q_D(const QWebGLWindow);
+ return d->format;
+}
+
+QWebGLScreen *QWebGLWindow::screen() const
+{
+ return static_cast<QWebGLScreen *>(QPlatformWindow::screen());
+}
+
+void QWebGLWindow::setDefaults(const QMap<GLenum, QVariant> &values)
+{
+ Q_D(QWebGLWindow);
+ d->defaults.set_value(values);
+}
+
+WId QWebGLWindow::winId() const
+{
+ Q_D(const QWebGLWindow);
+ return d->id;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/webgl/qwebglwindow.h b/src/plugins/platforms/webgl/qwebglwindow.h
new file mode 100644
index 0000000..972207c
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglwindow.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLWINDOW_H
+#define QWEBGLWINDOW_H
+
+#include "qwebglintegration.h"
+#include "qwebglscreen.h"
+
+#include <QtCore/qscopedpointer.h>
+#include <QtGui/qpa/qplatformwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLCompositorBackingStore;
+
+class QWebGLWindowPrivate;
+
+class QWebGLWindow : public QPlatformWindow
+{
+ friend class QWebGLIntegrationPrivate;
+ friend class QWebGLContext;
+ friend class QWebGLWebSocketServer;
+
+public:
+ QWebGLWindow(QWindow *w);
+ ~QWebGLWindow() override;
+
+ void create();
+ void destroy();
+
+ void raise() override;
+
+ WId winId() const override;
+
+ QSurfaceFormat format() const override;
+
+ QWebGLScreen *screen() const;
+
+ void setDefaults(const QMap<GLenum, QVariant> &values);
+
+private:
+ Q_DISABLE_COPY(QWebGLWindow)
+ Q_DECLARE_PRIVATE(QWebGLWindow)
+ QScopedPointer<QWebGLWindowPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLWINDOW_H
diff --git a/src/plugins/platforms/webgl/qwebglwindow_p.h b/src/plugins/platforms/webgl/qwebglwindow_p.h
new file mode 100644
index 0000000..280733d
--- /dev/null
+++ b/src/plugins/platforms/webgl/qwebglwindow_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt WebGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBGLWINDOW_P_H
+#define QWEBGLWINDOW_P_H
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qpointer.h>
+#include <QtGui/qsurfaceformat.h>
+
+#if (defined(Q_CC_MSVC) && _MSC_VER <= 1800)
+// https://connect.microsoft.com/VisualStudio/feedback/details/811347/compiling-vc-12-0-with-has-exceptions-0-and-including-concrt-h-causes-a-compiler-error
+_CRTIMP bool __cdecl __uncaught_exception();
+#endif
+
+#include <future>
+
+QT_BEGIN_NAMESPACE
+
+class QWebGLWindow;
+
+class QWebGLWindowPrivate
+{
+public:
+ QWebGLWindowPrivate(QWebGLWindow *p);
+
+ bool raster = false;
+ QSurfaceFormat format;
+
+ enum Flag {
+ Created = 0x01,
+ HasNativeWindow = 0x02,
+ IsFullScreen = 0x04
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+ Flags flags;
+
+ std::promise<QMap<unsigned int, QVariant>> defaults;
+ int id = -1;
+ static QAtomicInt nextId;
+
+private:
+ Q_DECLARE_PUBLIC(QWebGLWindow)
+ QWebGLWindow *q_ptr = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBGLWINDOW_P_H
diff --git a/src/plugins/platforms/webgl/webgl.json b/src/plugins/platforms/webgl/webgl.json
new file mode 100644
index 0000000..ff9bfef
--- /dev/null
+++ b/src/plugins/platforms/webgl/webgl.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "webgl" ]
+}
diff --git a/src/plugins/platforms/webgl/webgl.pro b/src/plugins/platforms/webgl/webgl.pro
new file mode 100644
index 0000000..034e091
--- /dev/null
+++ b/src/plugins/platforms/webgl/webgl.pro
@@ -0,0 +1,45 @@
+TARGET = qwebgl
+QT += \
+ websockets \
+ gui-private \
+ eventdispatcher_support-private \
+ fontdatabase_support-private \
+ platformcompositor_support-private \
+ theme_support-private
+
+qtHaveModule(quick) {
+ QT += quick
+}
+
+HEADERS += \
+ qwebglcontext.h \
+ qwebglfunctioncall.h \
+ qwebglhttpserver.h \
+ qwebglintegration.h \
+ qwebglintegration_p.h \
+ qwebglplatformservices.h \
+ qwebglscreen.h \
+ qwebglwebsocketserver.h \
+ qwebglwindow.h \
+ qwebglwindow_p.h
+
+SOURCES += \
+ qwebglcontext.cpp \
+ qwebglfunctioncall.cpp \
+ qwebglhttpserver.cpp \
+ qwebglintegration.cpp \
+ qwebglmain.cpp \
+ qwebglplatformservices.cpp \
+ qwebglscreen.cpp \
+ qwebglwebsocketserver.cpp \
+ qwebglwindow.cpp
+
+RESOURCES += \
+ webgl.qrc
+
+DISTFILES += webgl.json
+
+PLUGIN_TYPE = platforms
+PLUGIN_CLASS_NAME = QWebGLIntegrationPlugin
+!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
+load(qt_plugin)
diff --git a/src/plugins/platforms/webgl/webgl.qrc b/src/plugins/platforms/webgl/webgl.qrc
new file mode 100644
index 0000000..9861f25
--- /dev/null
+++ b/src/plugins/platforms/webgl/webgl.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/webgl">
+ <file>favicon.ico</file>
+ <file>index.html</file>
+ <file>webqt.jsx</file>
+ </qresource>
+</RCC>
diff --git a/src/plugins/platforms/webgl/webqt.jsx b/src/plugins/platforms/webgl/webqt.jsx
new file mode 100644
index 0000000..fa5b64c
--- /dev/null
+++ b/src/plugins/platforms/webgl/webqt.jsx
@@ -0,0 +1,1212 @@
+function getBrowserSize() {
+ var actualWidth = window.innerWidth ||
+ document.documentElement.clientWidth ||
+ document.body.clientWidth ||
+ document.body.offsetWidth;
+ var actualHeight = window.innerHeight ||
+ document.documentElement.clientHeight ||
+ document.body.clientHeight ||
+ document.body.offsetHeight;
+ return { "width": actualWidth, "height" : actualHeight };
+}
+
+function getContext(canvas) {
+ var settings = { preserveDrawingBuffer: true };
+ gl = canvas.getContext("webgl", settings) ||
+ canvas.getContext("experimental-webgl", settings);
+ return gl;
+}
+
+function physicalSizeRatio() {
+ var div = document.createElement("div");
+ div.style.width = "1mm";
+ div.style.height = "1mm";
+ var body = document.getElementsByTagName("body")[0];
+ body.appendChild(div);
+ var physicalWidth = document.defaultView.getComputedStyle(div, null).getPropertyValue('width');
+ var physicalHeight = document.defaultView.getComputedStyle(div, null).getPropertyValue('height');
+ body.removeChild(div);
+ return {
+ 'width' : parseFloat(physicalWidth),
+ 'height' : parseFloat(physicalHeight)
+ };
+}
+
+window.onload = function () {
+ var DEBUG = 0;
+ var canvas;
+ var socket = new WebSocket("ws://" + host + ":" + port);
+ socket.binaryType = "arraybuffer";
+ var CONNECT_SERIAL = 666;
+ var gl;
+ var startTime = new Date();
+ // There is no way to get proper vsync since we have no idea when the real
+ // swap happens under the hood. What we can do is to delay the response for
+ // the eglSwapBuffer call, i.e. block the client for the given number of
+ // milliseconds on each swap.
+ var SWAP_DELAY = 16; //${swap_delay};
+ var contextData = { }; // context -> { shaderMap, programMap, ... }
+ var currentContext = 0;
+ var binaryDataBuffer = new Uint8Array(0);
+ var currentWindowId = "";
+ var windowData = {};
+ var currentZIndex = 1;
+ var textDecoder;
+ var initialLoadingCanvas;
+ if (typeof TextDecoder !== 'undefined') {
+ textDecoder = new TextDecoder("utf8");
+ } else {
+ textDecoder = {
+ "decode" : function (buffer)
+ {
+ var string = String.fromCharCode.apply(String, buffer);
+ return string;
+ }
+ };
+ }
+
+ var sendObject = function (obj) { socket.send(JSON.stringify(obj)); }
+
+ var connect = function () {
+ var size = getBrowserSize();
+ var width = size.width;
+ var height = size.height;
+ var physicalSize = physicalSizeRatio();
+
+ var object = { "type" : "connect",
+ "width" : width, "height" : height,
+ "physicalWidth" : width / physicalSize.width,
+ "physicalHeight" : height / physicalSize.height
+ };
+ sendObject(object);
+ initialLoadingCanvas = createLoadingCanvas('loadingCanvas', 0, 0, width, height);
+ };
+
+ var sendResponse = function (id, value) {
+ if (DEBUG)
+ console.log("Response to " + id + " = " + value);
+ sendObject({ "type": "gl_response", "id": id, "value": value });
+ };
+
+ var sendResize = function (width, height, physicalWidth, physicalHeight) {
+ if (DEBUG)
+ console.log("Resizing canvas to " + width + " x " + height);
+ var object = { "type": "canvas_resize",
+ "width": width, "height": height,
+ "physicalWidth" : physicalWidth, "physicalHeight" : physicalHeight
+ };
+ sendObject(socket);
+ };
+
+ var createLoadingCanvas = function(name, x, y, width, height) {
+ var canvas = document.createElement("canvas");
+ canvas.id = "loading_" + name;
+ canvas.style.position = "absolute";
+ canvas.style.left = x + "px";
+ canvas.style.top = y + "px";
+ canvas.style.width = width + "px";
+ canvas.style.height = height + "px";
+ canvas.style.zIndex = currentZIndex++;
+ canvas.style.background = "black";
+ canvas.width = width;
+ canvas.height = height;
+ var body = document.getElementsByTagName("body")[0];
+ body.appendChild(canvas);
+
+ var gl = canvas.getContext("webgl");
+
+ var loadingVertexShaderSource =
+ "attribute vec2 a_position; void main(){ gl_Position = vec4(a_position, 0, 1); }";
+ var loadingFragmentShaderSource =
+ "precision mediump float;\n"+
+ "#define PI 3.14159265\n"+
+ "#define QT_COLOR vec4(0.255,0.804,0.321,1.0)\n"+
+ "#define GRAY vec4(0.953,0.953,0.957,1.0)\n"+
+ "uniform float u_time;\n"+
+ "uniform vec2 u_size;\n"+
+ "vec4 loadingIndicator(vec2 uv, float time)\n"+
+ "{\n"+
+ " float l = length(uv);\n"+
+ " float theta = atan(uv.y,uv.x)/PI/2.0 + 0.5;\n"+
+ " float radius = 0.2;\n"+
+ " float thickness = 0.02;\n"+
+ " float edge = 3.0 / length(u_size.xy);\n"+
+ " float t = fract(-time*2.0);\n"+
+ " float circleVal = smoothstep(radius - edge - thickness, radius - thickness, l) * \n"+
+ " (1.0 - smoothstep(radius + thickness, radius + edge + thickness, l));\n"+
+ " float indicatorVal = smoothstep(0.245,0.25,fract(theta - t)) * (1.0 - smoothstep(0.995,1.0,fract(theta - t)));\n"+
+ " return (1.0 - indicatorVal) * QT_COLOR + indicatorVal * circleVal * GRAY + vec4(1.0,1.0,1.0,1.0) * (1.0 - circleVal);\n"+
+ "}\n"+
+ "void main()\n" +
+ "{\n"+
+ " vec2 aspect = vec2(u_size.x/u_size.y, 1.0);\n"+
+ " vec2 origin = aspect*vec2(0.5, 0.5);\n"+
+ " vec2 uv = aspect*gl_FragCoord.xy;\n"+
+ " gl_FragColor = loadingIndicator(uv/u_size - origin,u_time); \n"+
+ "}\n";
+
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, loadingVertexShaderSource);
+ gl.compileShader(vertexShader);
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, loadingFragmentShaderSource);
+ gl.compileShader(fragmentShader);
+
+ var program = gl.createProgram();
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+ gl.linkProgram(program);
+ gl.useProgram(program);
+
+ // look up where the vertex data needs to go.
+ var positionLocation = gl.getAttribLocation(program, "a_position");
+ var timeLocation = gl.getUniformLocation(program, "u_time");
+ var sizeLocation = gl.getUniformLocation(program, "u_size");
+
+ // Create a buffer and put a single clipspace rectangle in
+ // it (2 triangles)
+ var buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([
+ -1.0, -1.0,
+ 1.0, -1.0,
+ -1.0, 1.0,
+ -1.0, 1.0,
+ 1.0, -1.0,
+ 1.0, 1.0]),
+ gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(positionLocation);
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+ gl.uniform2fv(sizeLocation, new Float32Array([canvas.width, canvas.height]));
+ var time = 0.0;
+
+ function draw() {
+ if (canvas) {
+ gl.uniform1f(timeLocation, time);
+ time += 0.01;
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+ setTimeout(draw, 16);
+ }
+ }
+ draw();
+ return canvas;
+ };
+
+ var createCanvas = function (name, x, y, width, height, title) {
+ if (initialLoadingCanvas) {
+ var body = document.getElementsByTagName("body")[0];
+ body.removeChild(initialLoadingCanvas);
+ initialLoadingCanvas = undefined;
+ }
+ var canvas = document.createElement("canvas");
+ canvas.id = name;
+ canvas.style.position = "absolute";
+ canvas.style.left = x + "px";
+ canvas.style.top = y + "px";
+ canvas.style.width = width + "px";
+ canvas.style.height = height + "px";
+ canvas.style.zIndex = currentZIndex++;
+ canvas.width = width;
+ canvas.height = height;
+ var body = document.getElementsByTagName("body")[0];
+ body.appendChild(canvas);
+
+ var qtButtons = 0;
+ var sendMouseEvent = function (buttons, layerX, layerY, clientX, clientY, name) {
+ var object = { "type": "mouse",
+ "buttons": buttons,
+ "layerX": layerX, "layerY": layerY, "clientX" : clientX, "clientY" : clientY,
+ "time" : new Date().getTime(),
+ "name" : name
+ };
+ sendObject(object);
+ };
+
+ var mapButton = function (b) {
+ var qtb = 1;
+ if (b === 1)
+ qtb = 4;
+ else if (b === 2)
+ qtb = 2;
+ return qtb;
+ };
+
+ canvas.onmousedown = function (event) {
+ qtButtons |= mapButton(event.button);
+ sendMouseEvent(qtButtons, event.layerX, event.layerY, event.clientX, event.clientY,
+ name);
+ };
+
+ canvas.onmousemove = function (event) {
+ sendMouseEvent(qtButtons, event.layerX, event.layerY, event.clientX, event.clientY,
+ name);
+ };
+
+ canvas.onmouseup = function (event) {
+ qtButtons &= ~mapButton(event.button);
+ sendMouseEvent(qtButtons, event.layerX, event.layerY, event.clientX, event.clientY,
+ name);
+ };
+
+ function handleMouseWheel(event) {
+ var deltaY = 0;
+ if (!event)
+ deltaY = window.event;
+ if (event.deltaY)
+ deltaY = event.deltaY;
+ else if (event.detail)
+ deltaY = event.detail * 40;
+ if (deltaY) {
+ var object = { "type" : "wheel",
+ "layerX" : event.layerX, "layerY" : event.layerY,
+ "clientX" : event.clientX, "clientY" : event.clientY,
+ "deltaX" : event.deltaX, "deltaY" : deltaY, "deltaZ" : event.deltaZ,
+ "time" : new Date().getTime(),
+ "name" : name
+ };
+ sendObject(object);
+ }
+ if (event.preventDefault)
+ event.preventDefault();
+ event.returnValue = false;
+ }
+
+ if ("onmousewheel" in canvas)
+ canvas.onmousewheel = handleMouseWheel;
+ else
+ canvas.addEventListener('DOMMouseScroll', handleMouseWheel, false);
+
+ function handleTouch(event) {
+ var object = {};
+ object["type"] = "touch";
+ object["name"] = name;
+ object["time"] = new Date().getTime();
+ object["event"] = event.type;
+ object["changedTouches"] = [];
+ for (var i = 0; i < event.targetTouches.length; ++i) {
+ var changedTouch = event.targetTouches[i];
+ var touch = {};
+ touch["clientX"] = changedTouch.clientX;
+ touch["clientY"] = changedTouch.clientY;
+ touch["force"] = changedTouch.force;
+ touch["identifier"] = changedTouch.identifier;
+ touch["pageX"] = changedTouch.pageX;
+ touch["pageY"] = changedTouch.pageY;
+ touch["radiousX"] = changedTouch.radiousX;
+ touch["radiousY"] = changedTouch.radiousY;
+ touch["rotatingAngle"] = changedTouch.rotatingAngle;
+ touch["screenX"] = changedTouch.screenX;
+ touch["screenY"] = changedTouch.screenY;
+ touch["normalPositionX"] = changedTouch.screenX / screen.width;
+ touch["normalPositionY"] = changedTouch.screenY / screen.height;
+ object.changedTouches.push(touch);
+ }
+ sendObject(object);
+
+ if (event.preventDefault && event.cancelable)
+ event.preventDefault();
+ event.returnValue = false;
+ }
+
+ canvas.addEventListener("touchstart", handleTouch, false);
+ canvas.addEventListener("touchend", handleTouch, false);
+ canvas.addEventListener("touchcancel", handleTouch, false);
+ canvas.addEventListener("touchmove", handleTouch, false);
+
+ canvas.oncontextmenu = function (event) {
+ event.preventDefault();
+ };
+
+
+ var gl = getContext(canvas);
+ gl.clear([ gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT]);
+ var data = windowData[name] = {
+ "canvas" : canvas,
+ "gl" : gl,
+ "loadingCanvas" : createLoadingCanvas(name, x, y, width, height)
+ };
+
+ var defaultValuesObject = { 'type' : 'default_context_parameters', 'name' : name,
+ '7939' : "GL_OES_element_index_uint GL_OES_standard_derivatives " + // GL_EXTENSIONS
+ "GL_OES_depth_texture GL_OES_packed_depth_stencil" };
+ [
+// gl.ACTIVE_TEXTURE,
+// gl.ALIASED_LINE_WIDTH_RANGE,
+// gl.ALIASED_POINT_SIZE_RANGE,
+// gl.ALPHA_BITS,
+ gl.BLEND,
+// gl.BLEND_COLOR,
+// gl.BLEND_DST_ALPHA,
+// gl.BLEND_DST_RGB,
+// gl.BLEND_EQUATION,
+// gl.BLEND_EQUATION_ALPHA,
+// gl.BLEND_EQUATION_RGB,
+// gl.BLEND_SRC_ALPHA,
+// gl.BLEND_SRC_RGB,
+// gl.BLUE_BITS,
+// gl.COLOR_CLEAR_VALUE,
+// gl.COLOR_WRITEMASK,
+// gl.COMPRESSED_TEXTURE_FORMATS,
+// gl.CULL_FACE,
+// gl.CULL_FACE_MODE,
+// gl.DEPTH_BITS,
+// gl.DEPTH_CLEAR_VALUE,
+// gl.DEPTH_FUNC,
+// gl.DEPTH_RANGE,
+ gl.DEPTH_TEST,
+// gl.DEPTH_WRITEMASK,
+// gl.DITHER,
+// gl.FRONT_FACE,
+// gl.GENERATE_MIPMAP_HINT,
+// gl.GREEN_BITS,
+// gl.IMPLEMENTATION_COLOR_READ_FORMAT,
+// gl.IMPLEMENTATION_COLOR_READ_TYPE,
+// gl.LINE_WIDTH,
+// gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+// gl.MAX_CUBE_MAP_TEXTURE_SIZE,
+// gl.MAX_FRAGMENT_UNIFORM_VECTORS,
+// gl.MAX_RENDERBUFFER_SIZE,
+// gl.MAX_TEXTURE_IMAGE_UNITS,
+ gl.MAX_TEXTURE_SIZE,
+// gl.MAX_VARYING_VECTORS,
+ gl.MAX_VERTEX_ATTRIBS,
+// gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS,
+// gl.MAX_VERTEX_UNIFORM_VECTORS,
+// gl.MAX_VIEWPORT_DIMS,
+// gl.PACK_ALIGNMENT,
+// gl.POLYGON_OFFSET_FACTOR,
+// gl.POLYGON_OFFSET_FILL,
+// gl.POLYGON_OFFSET_UNITS,
+// gl.RED_BITS,
+ gl.RENDERER,
+// gl.SAMPLE_BUFFERS,
+// gl.SAMPLE_COVERAGE_INVERT,
+// gl.SAMPLE_COVERAGE_VALUE,
+// gl.SAMPLES,
+// gl.SCISSOR_BOX,
+ gl.SCISSOR_TEST,
+// gl.SHADING_LANGUAGE_VERSION,
+// gl.STENCIL_BACK_FAIL,
+// gl.STENCIL_BACK_FUNC,
+// gl.STENCIL_BACK_PASS_DEPTH_FAIL,
+// gl.STENCIL_BACK_PASS_DEPTH_PASS,
+// gl.STENCIL_BACK_REF,
+// gl.STENCIL_BACK_VALUE_MASK,
+// gl.STENCIL_BACK_WRITEMASK,
+// gl.STENCIL_BITS,
+// gl.STENCIL_CLEAR_VALUE,
+// gl.STENCIL_FAIL,
+// gl.STENCIL_FUNC,
+// gl.STENCIL_PASS_DEPTH_FAIL,
+// gl.STENCIL_PASS_DEPTH_PASS,
+// gl.STENCIL_REF,
+ gl.STENCIL_TEST,
+// gl.STENCIL_VALUE_MASK,
+// gl.STENCIL_WRITEMASK,
+// gl.SUBPIXEL_BITS,
+ gl.UNPACK_ALIGNMENT,
+// gl.UNPACK_COLORSPACE_CONVERSION_WEBGL,
+// gl.UNPACK_FLIP_Y_WEBGL,
+// gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
+ gl.VENDOR,
+ gl.VERSION,
+ gl.VIEWPORT
+ ].forEach(function (value) {
+ defaultValuesObject[value] = gl.getParameter(value);
+ });
+ sendObject(defaultValuesObject);
+
+ gl._attachShader = gl.attachShader;
+ gl.attachShader = function(program, shader) {
+ var d = contextData[currentContext];
+ gl._attachShader(d.programMap[program], d.shaderMap[shader].shader);
+ };
+
+ gl._bindAttribLocation = gl.bindAttribLocation;
+ gl.bindAttribLocation = function(program, index, name) {
+ var d = contextData[currentContext];
+ gl._bindAttribLocation(d.programMap[program], index, name);
+ };
+
+ gl._bindBuffer = gl.bindBuffer;
+ gl.bindBuffer = function(target, buffer) {
+ var d = contextData[currentContext];
+ gl._bindBuffer(target, buffer ? d.bufferMap[buffer] : null);
+ };
+
+ gl._bindFramebuffer = gl.bindFramebuffer;
+ gl.bindFramebuffer = function(target, framebuffer) {
+ var d = contextData[currentContext];
+ gl._bindFramebuffer(target, framebuffer ? d.framebufferMap[framebuffer] : null);
+ };
+
+ gl._bindRenderbuffer = gl.bindRenderbuffer;
+ gl.bindRenderbuffer = function(target, renderbuffer) {
+ var d = contextData[currentContext];
+ gl._bindRenderbuffer(target, renderbuffer ? d.renderbufferMap[renderbuffer] : null);
+ d.boundRenderbuffer = renderbuffer;
+ };
+
+ gl._bindTexture = gl.bindTexture;
+ gl.bindTexture = function(target, texture) {
+ gl._bindTexture(target, texture ? mapTexture(currentContext, texture) : null);
+ };
+
+ gl._bufferData = gl.bufferData;
+ gl.bufferData = function(target, usage, size, data) {
+ gl._bufferData(target, data.length === 0 ? size : data, usage);
+ };
+
+ gl._clearColor = gl.clearColor;
+ gl.clearColor = function (red, green, blue, alpha) {
+ gl._clearColor(red, green, blue, alpha);
+ }
+
+ gl.clearDepthf = function(depth) {
+ gl.clearDepth(depth);
+ };
+
+ gl._compileShader = gl.compileShader;
+ gl.compileShader = function(remoteShader) {
+ var d = contextData[currentContext];
+ gl._compileShader(d.shaderMap[remoteShader].shader);
+ };
+
+ gl._createProgram = gl.createProgram;
+ gl.createProgram = function() {
+ var d = contextData[currentContext];
+ var remoteProgram = d.nextProgramId++;
+ var localProgram = gl._createProgram();
+ d.programMap[remoteProgram] = localProgram;
+ return remoteProgram;
+ };
+
+ gl._createShader = gl.createShader;
+ gl.createShader = function(type) {
+ var d = contextData[currentContext];
+ var remoteShader = d.nextShaderId++;
+ var localShader = gl._createShader(type);
+ d.shaderMap[remoteShader] = { };
+ d.shaderMap[remoteShader].shader = localShader;
+ d.shaderMap[remoteShader].source = "";
+ return remoteShader;
+ };
+
+ gl.deleteBuffers = function(n) {
+ var d = contextData[currentContext];
+ for (var i = 0; i < n; ++i)
+ gl.deleteBuffer(d.bufferMap[arguments[1 +i]]);
+ };
+
+ gl._deleteFramebuffers = gl.deleteFramebuffers;
+ gl.deleteFramebuffers = function(n) {
+ var d = contextData[currentContext];
+ for (var i = 0; i < n; ++i)
+ gl.deleteFramebuffer(d.framebufferMap[arguments[1 + i]]);
+ };
+
+ gl._deleteProgram = gl.deleteProgram;
+ gl.deleteProgram = function(program) {
+ var d = contextData[currentContext];
+ gl._deleteProgram(d.programMap[program]);
+ };
+
+ gl._deleteRenderbuffers = gl.deleteRenderbuffers;
+ gl.deleteRenderbuffers = function() {
+ var d = contextData[currentContext];
+ for (var i in arguments)
+ gl.deleteRenderbuffer(d.renderbufferMap[arguments[i]]);
+ };
+
+ gl._deleteShader = gl.deleteShader;
+ gl.deleteShader = function(remoteShader) {
+ var d = contextData[currentContext];
+ gl._deleteShader(d.shaderMap[remoteShader].shader);
+ };
+
+ gl.deleteTextures = function(n) {
+ for (var i = 0; i < n; ++i)
+ gl.deleteTexture(mapTexture(currentContext, arguments[1 + i]));
+ };
+
+ gl._drawElements = gl.drawElements;
+ gl.drawElements = function(mode, count, type, n, indices, offset) {
+ if (!arguments[3].length)
+ gl._drawElements(mode, count, type, offset);
+ else
+ console.error("fixme: Client-side drawElements not supported");
+ };
+
+ gl._framebufferRenderbuffer = gl.framebufferRenderbuffer;
+ gl.framebufferRenderbuffer = function(target, attachment, renderbuffertarget, renderbuffer)
+ {
+ var d = contextData[currentContext];
+ // With packed depth-stencil Quick tries to attach the same renderbuffer to both
+ // the depth and stencil attachment points. WebGL does not allow this. Instead,
+ // we need to attach to the DEPTH_STENCIL attachment point.
+ if (d.renderbufferFormat[d.boundRenderbuffer] === gl.DEPTH_STENCIL) {
+ if (attachment === gl.STENCIL_ATTACHMENT)
+ attachment = gl.DEPTH_STENCIL_ATTACHMENT;
+ }
+ gl._framebufferRenderbuffer(target, attachment, renderbuffertarget,
+ d.renderbufferMap[renderbuffer]);
+ };
+
+ gl.genBuffers = function(n) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < n; ++i) {
+ var remoteBuf = d.nextBufferId++
+ var localBuf = gl.createBuffer();
+ data.push(remoteBuf);
+ d.bufferMap[remoteBuf] = localBuf;
+ }
+ return data;
+ };
+
+ gl.genFramebuffers = function(n) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < n; ++i) {
+ var remoteFramebuffer = d.nextFramebufferId++;
+ var localFramebuffer = gl.createFramebuffer();
+ d.framebufferMap[remoteFramebuffer] = localFramebuffer;
+ data.push(remoteFramebuffer);
+ }
+ return data;
+ };
+
+ gl._genRenderbuffers = gl.genRenderbuffers;
+ gl.genRenderbuffers = function(n) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < n; ++i) {
+ var remoteRenderBuffer = d.nextRenderBufferId++;
+ var localRenderBuffer = gl.createRenderbuffer();
+ d.renderbufferMap[remoteRenderBuffer] = localRenderBuffer;
+ data.push(remoteRenderBuffer);
+ }
+ return data;
+ };
+
+ gl.getAttachedShaders = function(program, maxCount) {
+ var d = contextData[currentContext];
+ var shaders = d.attachedShaderMap[program];
+ var data = [];
+ for (var shader in shaders) {
+ for (var remoteShaderId in shaderMap) {
+ if (shaderMap[remoteShaderId].shader === shader)
+ data.push(remoteShaderId);
+ }
+ }
+ return data;
+ };
+
+ gl._getAttribLocation = gl.getAttribLocation;
+ gl.getAttribLocation = function(program, name) {
+ if (typeof program === "object")
+ return gl._getAttribLocation(program, name);
+ var d = contextData[currentContext];
+ return gl._getAttribLocation(d.programMap[program], name);
+ };
+
+ gl.getBooleanv = gl.getParameter;
+ gl.getIntegerv = gl.getParameter;
+
+ gl.getProgramiv = function(program, pname) {
+ var d = contextData[currentContext];
+ if (pname === 0x8B84) // INFO_LOG_LENGTH
+ return gl._getProgramInfoLog(d.programMap[program]).length;
+ else
+ return gl.getProgramParameter(d.programMap[program], pname);
+ };
+
+ gl._getShaderInfoLog = gl.getShaderInfoLog;
+ gl.getShaderInfoLog = function(shader) {
+ if (typeof shader === "object")
+ return gl._getShaderInfoLog(shader);
+ var d = contextData[currentContext];
+ return gl._getShaderInfoLog(d.shaderMap[shader].shader);
+ };
+
+ gl.getShaderiv = function(shader, pname) {
+ var d = contextData[currentContext];
+ var p;
+ if (pname === 0x8B88)
+ return d.shaderMap[shader].source.length;
+ else
+ return gl.getShaderParameter(d.shaderMap[shader].shader, pname);
+ };
+
+ gl._getShaderSource = gl.getShaderSource;
+ gl.getShaderSource = function(remoteShader) {
+ var d = contextData[currentContext];
+ return gl._getShaderSource(d.shaderMap[remoteShader].shader);
+ };
+
+ gl.getString = function(pname) {
+ var result = "";
+ return gl.getParameter(pname);
+ };
+
+ gl._getProgramInfoLog = gl.getProgramInfoLog;
+ gl.getProgramInfoLog = function(remoteProgram) {
+ var d = contextData[currentContext];
+ var localProgram = d.programMap[remoteProgram];
+ return gl._getProgramInfoLog(localProgram);
+ };
+
+ gl._getUniformLocation = gl.getUniformLocation;
+ gl.getUniformLocation = function(program, name) {
+ if (typeof program === "object") {
+ return gl._getUniformLocation(program, name);
+ } else {
+ var d = contextData[currentContext];
+ var p = gl._getUniformLocation(d.programMap[program], name);
+ var location = -1;
+ if (p) {
+ location = d.nextLocation++;
+ d.uniformLocationMap[location] = p;
+ }
+ return location;
+ }
+ };
+
+ gl.genTextures = function(n) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < n; ++i) {
+ var remoteTexture = d.nextTextureId++;
+ var localTexture = gl.createTexture();
+ d.textureMap[remoteTexture] = localTexture;
+ data.push(remoteTexture);
+ }
+ return data;
+ };
+
+ gl._framebufferTexture2D = gl.framebufferTexture2D;
+ gl.framebufferTexture2D = function(target, attachment, texTarget, texture, level) {
+ var d = contextData[currentContext];
+ gl._framebufferTexture2D(target, attachment, texTarget, d.textureMap[texture], level);
+ };
+
+ gl._isRenderbuffer = gl.isRenderbuffer;
+ gl.isRenderbuffer = function(renderbuffer) {
+ if (typeof renderbuffer === "object")
+ return gl._isRenderBuffer(renderbuffer);
+ var d = contextData[currentContext];
+ return gl._isRenderbuffer(d.renderbufferMap[renderbuffer]);
+ }
+
+ gl._linkProgram = gl.linkProgram;
+ gl.linkProgram = function(program) {
+ var d = contextData[currentContext];
+ gl._linkProgram(d.programMap[program]);
+ };
+
+ gl._renderbufferStorage = gl.renderbufferStorage;
+ gl.renderbufferStorage = function(target, internalFormat, width, height) {
+ var d = contextData[currentContext];
+ if (internalFormat === 0x88F0) // GL_DEPTH24_STENCIL8_OES
+ internalformat = 0x84F9; // GL_DEPTH_STENCIL_OES
+ d.renderbufferFormat[d.boundRenderbuffer] = internalFormat;
+ gl._renderbufferStorage(target, internalFormat, width, height);
+ };
+
+ gl._texImage2D = gl.texImage2D;
+ gl.texImage2D = function(target, level, internalFormat, width, height, border, format, type,
+ data) {
+ var dataSize = data ? data.byteLength : 0;
+ var pixels;
+ if (data === null || dataSize === 0)
+ pixels = null;
+ else if (type === gl.UNSIGNED_BYTE)
+ pixels = data;
+ else if (type === g.UNSIGNED_SHORT_5_6_5 || type === UNSIGNED_SHORT_4_4_4_4 ||
+ type === UNSIGNED_SHORT_5_5_5_1)
+ pixels = new DataView(new Uint16Array(data));
+ else
+ console.error("gl.texImage2D: Unsupported type");
+ gl._texImage2D(target, level, internalFormat, width, height, border, format, type,
+ pixels);
+ };
+
+ gl._texSubImage2D = gl.texSubImage2D;
+ gl.texSubImage2D = function(target, level, xoffset, yoffset, width, height, format, type, data) {
+ var dataSize = data ? data.byteLength : 0;
+ var pixels;
+ if (data === null || dataSize === 0)
+ pixels = null;
+ if (type === gl.UNSIGNED_BYTE)
+ pixels = data;
+ else if (type === gl.UNSIGNED_SHORT_5_6_5 || type === gl.UNSIGNED_SHORT_4_4_4_4 ||
+ type === gl.UNSIGNED_SHORT_5_5_5_1 || type === ext.HALF_FLOAT_OES)
+ pixels = new Uint16Array(pixels);
+ else if (type === gl.FLOAT)
+ pixels = new Float32Array(pixels);
+ else
+ console.error("gl.texSubImage2D: Unsupported type");
+ gl._texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+ };
+
+ gl._shaderSource = gl.shaderSource;
+ gl.shaderSource = function(shader, count) {
+ var d = contextData[currentContext];
+ d.shaderMap[shader].source = "";
+ for (var i = 0; i < count; ++i)
+ d.shaderMap[shader].source += arguments[2 + i] + "\n";
+ gl._shaderSource(d.shaderMap[shader].shader, d.shaderMap[shader].source);
+ };
+
+ gl._uniform1f = gl.uniform1f;
+ gl.uniform1f = function(location, x) {
+ var d = contextData[currentContext];
+ gl._uniform1f(d.uniformLocationMap[location], x);
+ };
+
+ gl._uniform1fv = gl.uniform1fv;
+ gl.uniform1fv = function(location, count) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < count; ++i)
+ data.push(arguments[i + 2]);
+ gl._uniform1fv(d.uniformLocationMap[location], data);
+ };
+
+ gl._uniform2fv = gl.uniform2fv;
+ gl.uniform2fv = function(location, count) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < count * 2; ++i)
+ data.push(arguments[i + 2]);
+ gl._uniform2fv(d.uniformLocationMap[location], data);
+ };
+
+ gl._uniform3fv = gl.uniform3fv;
+ gl.uniform3fv = function(location, count) {
+ var d = contextData[context];
+ var data = [];
+ for (var i = 0; i < count * 3; ++i)
+ data.push(arguments[i + 2]);
+ gl._uniform3fv(d.uniformLocationMap[location], data);
+ };
+
+ gl._uniform1i = gl.uniform1i;
+ gl.uniform1i = function(location, value) {
+ var d = contextData[currentContext];
+ gl._uniform1i(d.uniformLocationMap[location], value);
+ };
+
+ gl._uniform4fv = gl.uniform4fv;
+ gl.uniform4fv = function(location, count) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < count * 4; ++i)
+ data.push(arguments[i + 2]);
+ gl._uniform4fv(d.uniformLocationMap[location], data);
+ };
+
+ gl._uniformMatrix3fv = gl.uniformMatrix3fv;
+ gl.uniformMatrix3fv = function(location, count, transpose) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < count * 9; ++i)
+ data.push(arguments[i + 3]);
+ gl._uniformMatrix3fv(d.uniformLocationMap[location], transpose, data);
+ };
+
+ gl._uniformMatrix4fv = gl.uniformMatrix4fv;
+ gl.uniformMatrix4fv = function(location, count, transpose) {
+ var d = contextData[currentContext];
+ var data = [];
+ for (var i = 0; i < count * 16; ++i)
+ data.push(arguments[i + 3]);
+ gl._uniformMatrix4fv(d.uniformLocationMap[location], transpose, data);
+ };
+
+ gl._useProgram = gl.useProgram;
+ gl.useProgram = function(program) {
+ var d = contextData[currentContext];
+ gl._useProgram(program !== 0 ? d.programMap[program] : null);
+ };
+
+ gl._vertexAttrib1fv = gl.vertexAttrib1fv;
+ gl.vertexAttrib1fv = function (index, v0) {
+ var values = new Float32Array([v0]);
+ gl._vertexAttrib1fv(index, values);
+ }
+
+ gl._vertexAttrib2fv = gl.vertexAttrib2fv;
+ gl.vertexAttrib2fv = function (index, v0, v1) {
+ var values = new Float32Array([v0, v1]);
+ gl._vertexAttrib2fv(index, values);
+ }
+
+ gl._vertexAttrib3fv = gl.vertexAttrib3fv;
+ gl.vertexAttrib3fv = function (index, v0, v1, v2) {
+ var values = new Float32Array([v0, v1, v2]);
+ gl._vertexAttrib3fv(index, values);
+ }
+
+ gl._drawArrays = gl.drawArrays;
+ gl.drawArrays = function (mode, first, count/*, size*/) {
+ var d = contextData[currentContext];
+ var subDataParts = [];
+ var bufferSize = 0;
+ var offset = 0;
+ if (!d.drawArrayBuf)
+ d.drawArrayBuf = gl.createBuffer();
+ gl._bindBuffer(gl.ARRAY_BUFFER, d.drawArrayBuf);
+ for (var i = 4; i < arguments.length; i += 6) {
+ var subData = {};
+ subData["index"] = arguments[i + 0];
+ subData["size"] = arguments[i + 1];
+ subData["type"] = arguments[i + 2];
+ subData["normalized"] = arguments[i + 3];
+ subData["stride"] = arguments[i + 4];
+ subData["offset"] = 0;
+ subData["data"] = arguments[i + 5];
+ subDataParts.push(subData);
+ bufferSize += subData.data.length;
+ }
+ gl._bufferData(gl.ARRAY_BUFFER, bufferSize, gl.STATIC_DRAW);
+ for (var part in subDataParts) {
+ gl.bufferSubData(gl.ARRAY_BUFFER, offset, subDataParts[part].data);
+ gl._vertexAttribPointer(subDataParts[part].index,
+ subDataParts[part].size,
+ subDataParts[part].type,
+ subDataParts[part].normalized,
+ subDataParts[part].stride,
+ offset);
+ offset += subDataParts[part].data.length;
+ }
+ gl._drawArrays(mode, first, count);
+ }
+
+ gl._vertexAttribPointer = gl.vertexAttribPointer;
+ gl.vertexAttribPointer = function (index, size, type, normalized, stride, pointer) {
+ gl._vertexAttribPointer(index, size, type, normalized, stride, pointer);
+ }
+
+
+ }
+
+ var commandsNeedingResponse = [
+ "checkFramebufferStatus",
+ "createProgram",
+ "createShader",
+ "genBuffers",
+ "genFramebuffers",
+ "genRenderbuffers",
+ "genTextures",
+ "getAttachedShaders",
+ "getAttribLocation",
+ "getBooleanv",
+ "getError",
+ "getFramebufferAttachmentParameteriv",
+ "getIntegerv",
+ "getParameter",
+ "getProgramInfoLog",
+ "getProgramiv",
+ "getRenderbufferParameteriv",
+ "getShaderiv",
+ "getShaderPrecisionFormat",
+ "getString",
+ "getTexParameterfv",
+ "getTexParameteriv",
+ "getUniformfv",
+ "getUniformLocation",
+ "getUniformiv",
+ "getVertexAttribfv",
+ "getVertexAttribiv",
+ "getShaderSource",
+ "getShaderInfoLog",
+ "isRenderbuffer"
+ ];
+
+ var ensureContextData = function (context) {
+ if (!(context in contextData)) {
+ contextData[context] = {
+ shaderMap: { },
+ programMap: { },
+ textureMap: { },
+ framebufferMap: { },
+ renderbufferMap: { },
+ renderbufferFormat: { },
+ boundRenderbuffer: 0,
+ bufferMap: { },
+ uniformLocationMap: { },
+ nextLocation: 1,
+ nextBufferId: 1,
+ nextProgramId: 1,
+ nextShaderId: 1,
+ nextFramebufferId: 1,
+ nextRenderBufferId: 1,
+ nextTextureId: 1,
+ pendingBinary: [],
+ drawArrayBuf: null,
+ drawArrayBufSize: 0,
+ glCommands: [],
+ attribData: []
+ };
+ }
+ };
+
+ var mapTexture = function (context, localId) {
+ var d = contextData[context];
+ if (localId in d.textureMap)
+ return d.textureMap[localId];
+ // ### do we need sharing?
+ console.error("Texture " + localId + " is not found in context " + context);
+ return 0;
+ };
+
+ var execGL = function (context) {
+ var d = contextData[context];
+ if (DEBUG)
+ console.log("executing " + d.glCommands.length + " commands");
+ while (d.glCommands.length) {
+ var obj = d.glCommands.shift();
+ if (DEBUG)
+ console.log("Calling: gl." + obj.function, obj.parameters);
+ var response = gl[obj.function].apply(gl, obj.parameters);
+ if (response !== undefined)
+ sendResponse(obj.id, response);
+ }
+ };
+
+ var injectGL = function (context, funcName, parameters) {
+ contextData[context].glCommands.push({ "function": funcName, "parameters": parameters });
+ };
+
+ var handleBinaryMessage = function (event) {
+ var appendBuffer = function(buffer1, buffer2) {
+ var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
+ tmp.set(new Uint8Array(buffer1), 0);
+ tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
+ return tmp.buffer;
+ };
+
+ var buffer = appendBuffer(binaryDataBuffer, event.data);
+ var view = new DataView(buffer);
+ if (view.getUint32(buffer.byteLength - 4) !== 0xbaadf00d) {
+ binaryDataBuffer = buffer;
+ return;
+ }
+ binaryDataBuffer = new ArrayBuffer(0);
+
+ var offset = 0;
+ var obj = { "parameters" : [] };
+ obj["id"] = view.getUint32(offset);
+ offset += 4;
+ obj["functionNameSize"] = view.getUint32(offset);
+ offset += 4;
+ obj["function"] = textDecoder.decode(new Uint8Array(buffer, offset, obj.functionNameSize));
+ offset += obj.functionNameSize;
+ obj["parameterCount"] = view.getUint32(offset);
+ offset += 4;
+ for (var i = 0; i < obj.parameterCount; ++i) {
+ var character = view.getUint8(offset);
+ offset += 1;
+ var parameterType = String.fromCharCode(character);
+ if (parameterType === 'i') {
+ obj.parameters.push(view.getInt32(offset));
+ offset += 4;
+ } else if (parameterType === 'u') {
+ obj.parameters.push(view.getUint32(offset));
+ offset += 4;
+ } else if (parameterType === 'd') {
+ obj.parameters.push(view.getFloat64(offset));
+ offset += 8;
+ } else if (parameterType === 'b') {
+ obj.parameters.push(view.getUint8(offset) === 1);
+ offset += 1;
+ break;
+ } else if (parameterType === 's') {
+ var stringSize = view.getUint32(offset);
+ offset += 4;
+ var string = textDecoder.decode(new Uint8Array(buffer, offset, stringSize));
+ obj.parameters.push(string);
+ offset += stringSize;
+ } else if (parameterType === 'x') {
+ var dataSize = view.getUint32(offset);
+ offset += 4;
+ var data = new Uint8Array(buffer, offset, dataSize);
+ var bytesRead = data.byteLength;
+ if (bytesRead !== dataSize)
+ console.error("invalid data");
+ obj.parameters.push(data);
+ offset += dataSize;
+ } else if (parameterType === 'n') {
+ obj.parameters.push(null);
+ }
+ }
+ var magic = view.getUint32(offset);
+ if (magic !== 0xbaadf00d)
+ console.error('Invalid magic');
+ offset += 4;
+ if (offset !== buffer.byteLength)
+ console.error("Invalid buffer")
+
+ if (!("function" in obj)) {
+ console.error("Function not found");
+ } else if (obj.function === "makeCurrent") {
+ var winId = obj.parameters[3];
+ if (winId in windowData) {
+ canvas = windowData[winId].canvas;
+ canvas.width = canvas.style.width = obj.parameters[1];
+ canvas.height = canvas.style.height = obj.parameters[2];
+ gl = windowData[winId].gl;
+ currentWindowId = winId;
+ currentContext = obj.parameters[0];
+ if (DEBUG)
+ console.log("Current context is now " + currentContext);
+ if (currentContext)
+ ensureContextData(currentContext);
+ }
+ } else if (obj.function === "swapBuffers") {
+ var data = windowData[currentWindowId];
+ if (data.loadingCanvas) {
+ var body = document.getElementsByTagName("body")[0];
+ body.removeChild(data.loadingCanvas);
+ data.loadingCanvas = undefined;
+ }
+
+ if (DEBUG)
+ var t0 = performance.now();
+ execGL(currentContext);
+ if (startTime) {
+ console.log((new Date() - startTime) + "ms to first frame.")
+ startTime = undefined;
+ }
+ var frameTime = performance.now() - t0;
+ if (DEBUG)
+ console.log("Swap time: " + frameTime + " ms.");
+ setTimeout((function () { sendResponse(obj.id, 1); }),
+ Math.max(SWAP_DELAY - frameTime, 0));
+ // have preserved swap and now we need to clear for real
+ } else {
+ handleGlesMessage(obj);
+ }
+ }
+
+ var handleGlesMessage = function (obj) {
+ // A GLES call. Unfortunately WebGL swaps when the control gets back to
+ // the event loop. This is totally retarded. So we queue the commands up
+ // and issue them when receiving a function that needs a response. This
+ // does not solve the problem and still needs preserved swap to be safe
+ // since eglSwapBuffers is not the only command that expects a response,
+ // but is more efficient than issuing everything from here.
+ var d = contextData[currentContext];
+ if (d)
+ d.glCommands.push(obj);
+ for (var i in commandsNeedingResponse)
+ if (commandsNeedingResponse[i] === obj.function) {
+ execGL(currentContext);
+ break;
+ }
+ };
+
+ socket.onopen = function (event) {
+ console.log("Socket Open");
+ (function(){
+ var doCheck = true;
+ var check = function(){
+ var size = getBrowserSize();
+ var width = size.width;
+ var height = size.height;
+ var physicalSize = physicalSizeRatio();
+
+ var object = { "type" : "canvas_resize",
+ "width" : width, "height" : height,
+ "physicalWidth" : width / physicalSize.width,
+ "physicalHeight" : height / physicalSize.height
+ };
+ sendObject(object);
+ };
+ window.addEventListener("resize",(function(){
+ if(doCheck){
+ check();
+ doCheck = false;
+ setTimeout((function(){
+ doCheck = true;
+ check();
+ }), 1000)
+ }
+ }));
+ })();
+ connect();
+ };
+ socket.onclose = function (event) {
+ console.log("Socket Closed (" + event.code + "): " + event.reason);
+ };
+ socket.onerror = function (error) {
+ console.log("Socket error: " + error.toString());
+ };
+ socket.onmessage = function (event) {
+ if (event.data instanceof ArrayBuffer) {
+ handleBinaryMessage(event);
+ return;
+ }
+ var obj;
+ try {
+ obj = JSON.parse(event.data);
+ } catch (e) {
+ console.error("Failed to parse " + event.data + ": " + e.toString());
+ return;
+ }
+ if (!("type" in obj)) {
+ console.error("Message type not found");
+ } else if (obj.type === "create_canvas") {
+ createCanvas(obj.winId, obj.x, obj.y, obj.width, obj.height, obj.title);
+ if (obj.title && obj.title.length)
+ document.title = obj.title;
+ } else if (obj.type === "destroy_canvas") {
+ var canvas = document.getElementById(obj.winId);
+ var body = document.getElementsByTagName("body")[0];
+ body.removeChild(canvas);
+ } else if (obj.type === "clipboard_updated") {
+ // Opens a new window/tab and shows the current remote clipboard. There is no way to
+ // copy some text to the local clipboard without user interaction.
+ window.open("/clipboard", "clipboard");
+ } else if (obj.type === "open_url") {
+ window.open(obj.url);
+ } else if (obj.type === "change_title") {
+ document.title = obj.text;
+ } else if (obj.type === "connect") {
+ var sysinfo = obj;
+ delete sysinfo["type"];
+ console.log(sysinfo);
+ } else {
+ console.error("Unknown message type");
+ }
+ };
+
+ var setupInput = function () {
+ var keyHandler = function (event) {
+ var object = { "type" : event.type,
+ "char" : event.char,
+ "key" : event.key,
+ "which" : event.which,
+ "location" : event.location,
+ "repeat" : event.repeat,
+ "locale" : event.locale,
+ "ctrlKey" : event.ctrlKey, "shiftKey" : event.shiftKey, "altKey" : event.altKey,
+ "metaKey" : event.metaKey,
+ "string" : String.fromCharCode(event.which ||
+ event.keyCode),
+ "keyCode" : event.keyCode, "charCode" : event.charCode,
+ "time" : new Date().getTime(),
+ };
+ sendObject(object);
+ }
+
+ document.addEventListener('keypress', keyHandler, true);
+ document.addEventListener('keydown', keyHandler, true);
+ document.addEventListener('keyup', keyHandler, true);
+ };
+ setupInput();
+};
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
new file mode 100644
index 0000000..878fc4c
--- /dev/null
+++ b/src/plugins/plugins.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ platforms
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..80734b5
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+
+SUBDIRS = \
+ plugins
diff --git a/sync.profile b/sync.profile
new file mode 100644
index 0000000..893ccb4
--- /dev/null
+++ b/sync.profile
@@ -0,0 +1,6 @@
+%modules = ( # path to module name map
+);
+
+%moduleheaders = ( # restrict the module headers to those found in relative path
+);
+
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644
index 0000000..566e172
--- /dev/null
+++ b/tests/tests.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+