diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..94a9ed024d3859793618152ea559a168bbcbb5e2 --- /dev/null +++ b/COPYING @@ -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/data/Info.plist b/data/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..5331787a5d39661f6b2f5042e88ad1b4dd7b7455 --- /dev/null +++ b/data/Info.plist @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>controllar</string> + <key>CFBundleIconFile</key> + <string>controllar.icns</string> + <key>CFBundleIdentifier</key> + <string>net.hitmuri.www</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>ControllAR</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>0.0.1</string> + <key>LSMinimumSystemVersion</key> + <string>10.4</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> + diff --git a/data/controllar.icns b/data/controllar.icns new file mode 100644 index 0000000000000000000000000000000000000000..a75da2a2869f74f7ca265973366d9e165cf6df81 Binary files /dev/null and b/data/controllar.icns differ diff --git a/data/controllar.xml b/data/controllar.xml new file mode 100755 index 0000000000000000000000000000000000000000..0991c65d369dece3f12ae7cf93acd9c1d145caa1 --- /dev/null +++ b/data/controllar.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-vjpirate-file"> + <comment>VjPirate file</comment> + <glob pattern="*.vjp"/> + <generic-icon name="vjp"/> + </mime-type> +</mime-info> diff --git a/data/controllar128.png b/data/controllar128.png new file mode 100644 index 0000000000000000000000000000000000000000..5006ce7874f071fb4f9c6a7fc39539a2040b01a3 Binary files /dev/null and b/data/controllar128.png differ diff --git a/data/controllar16.png b/data/controllar16.png new file mode 100644 index 0000000000000000000000000000000000000000..a8c0ddfcdf01122b8ba178b115486c8f718bcc53 Binary files /dev/null and b/data/controllar16.png differ diff --git a/data/controllar256.png b/data/controllar256.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c8373e05e7e56d58d8cea0c1738283792b7f27 Binary files /dev/null and b/data/controllar256.png differ diff --git a/data/controllar32.png b/data/controllar32.png new file mode 100644 index 0000000000000000000000000000000000000000..cda0544d56cdabf242d4fd643f7ad4493018da79 Binary files /dev/null and b/data/controllar32.png differ diff --git a/data/controllar48.png b/data/controllar48.png new file mode 100644 index 0000000000000000000000000000000000000000..ac1febf8d339a5e5a83f9fb1ef10fdf0faef3b84 Binary files /dev/null and b/data/controllar48.png differ diff --git a/data/controllar512.png b/data/controllar512.png new file mode 100644 index 0000000000000000000000000000000000000000..773cc2ec98f7f9d74359886586d4dfd4f5372d00 Binary files /dev/null and b/data/controllar512.png differ diff --git a/data/vjpirate.desktop b/data/vjpirate.desktop new file mode 100755 index 0000000000000000000000000000000000000000..65995b50a2f7d6604bba78174eb8f737449e8b67 --- /dev/null +++ b/data/vjpirate.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=ControllAR +Comment=Remixing visual feedback +Icon=controllar +Type=Application +Exec=controllar %f +FilePattern=controllar; +Terminal=false +StartupNotify=false +Categories=Audio;AudioVideo;Application; +GenericName=ControllAR +MimeType=application/x-controllar-file diff --git a/example.car b/example.car new file mode 100644 index 0000000000000000000000000000000000000000..07bb594de78d81be2bc535852cee1b0b13d5edf4 --- /dev/null +++ b/example.car @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ControllAR> + <Scene/> + <Zone win_name="pipeline.tex - QtikZ" win_x="70" win_y="67" win_w="87" win_h="83" zone_x="159" zone_y="94" zone_w="491" zone_h="434" alpha="255" transform="1" shape="1" effect="0" midi_type="0" midi_channel="32" midi_control="32752" color="0" visible="-1" update="-1" content="0"/> + <Zone win_name="xfce4-panel" win_x="0" win_y="0" win_w="241" win_h="36" zone_x="11" zone_y="54" zone_w="241" zone_h="194" alpha="255" transform="0" shape="0" effect="0" midi_type="0" midi_channel="16777216" midi_control="21848" color="0" visible="-1" update="-1" content="0"/> + <Midi device="" set_channel="0" set_type="0" set_control="0"/> +</ControllAR> diff --git a/src/Controlar.cpp b/src/Controlar.cpp new file mode 100755 index 0000000000000000000000000000000000000000..c3aae16f18502c84f9e10116cff22bdd0369d085 --- /dev/null +++ b/src/Controlar.cpp @@ -0,0 +1,1664 @@ +/*************************************************************************** + * Controlar.cpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "Controlar.hpp" + +#include <iostream> +#include <sstream> +#include <stdio.h> +#include <inttypes.h> +#include <unistd.h> +#include <portmidi.h> +#include <porttime.h> +#include <FL/fl_ask.H> +#include <FL/Fl.H> +#include "osc/osc/OscReceivedElements.h" +#include "osc/osc/OscOutboundPacketStream.h" +#include "MainPanel.hpp" +#include "ZonePanel.hpp" + +#ifdef OSX + #include "mac/MacWindowsManager.hpp" +#else + #ifdef WINDOWS + #include "win/WinWindowsManager.hpp" + #else + #include "x11/XWindowsManager.hpp" + #endif +#endif + +using namespace std; + +void idleFunc(void* data) { + Controlar::getInstance()->update(); + usleep(10000); +} + +void* oscThreadFunction(void* pvParam) { + Controlar::getInstance()->m_socket->Run(); + return 0; +} + +#ifdef GL +Controlar::Controlar(): Fl_Gl_Window(800, 600, "ControllAR") { +#else +Controlar::Controlar(): Fl_Double_Window(800, 600, "ControllAR") { +#endif + Fl::set_color(FL_BACKGROUND_COLOR, 200, 200, 200); + Fl::set_color(FL_BACKGROUND2_COLOR, 150, 150, 150); + Fl::set_color(FL_SELECTION_COLOR, 250, 250, 250); + box(FL_FLAT_BOX); + end(); + + m_drawing=false; + m_flipped=false; + m_fullscreen=false; + m_duplicating=false; + m_midiLearning=false; + m_askedScene=-1; + m_displayScale=1; + + m_alphaLevels[0] = 65; + m_alphaLevels[1] = 125; + m_alphaLevels[2] = 190; + m_alphaLevels[3] = 255; + for(int i=0;i<10;++i) { + m_menuValues[i]=i; + } + + //add a first set + //m_currentSet=0; + //m_zonesSets.push_back(map<unsigned int, ZoneWidget*>()); + m_scenes[0]="scene"; + m_currentScene=0; + + //create menus + m_zoneMenu = new FlipMenu(); + m_zoneMenu->hide(); + m_zoneMenu->end(); + add(m_zoneMenu); + m_mainMenu = new FlipMenu(); + m_mainMenu->end(); + m_mainMenu->hide(); + add(m_mainMenu); + m_mainMenu->addWithSub("File/Open", statOpen, this); + m_mainMenu->addWithSub("File/Save", statSave, this); + m_mainMenu->addWithSub("File/Save As", statSaveAs, this); + m_mainMenu->addWithSub("View/FullScreen", statFullscreen, this); + m_mainMenu->addWithSub("View/Mirror", statFlip, this); + m_mainMenu->addWithSub("Scenes/Next", statSceneNext, this); + m_mainMenu->addWithSub("Scenes/Prev", statScenePrev, this); + m_mainMenu->addWithSub("Scenes/Delete", statSceneDel, this); + m_mainMenu->addWithSub("Scenes/Learn Input", statSceneLearn, this); + m_mainMenu->addSub("Midi Input"); + m_mainMenu->add("Quit", statQuit, this); + + m_mainPanel = MainPanel::getInstance(); + //add(m_mainPanel); + m_mainPanel->hide(); + m_zonePanel= ZonePanel::getInstance(); + //add(m_zonePanel); + m_zonePanel->hide(); + + //initialize main loop function + Fl::add_idle(idleFunc, NULL); + + //create windows manager and get display scale if needed + #ifdef OSX + m_winsMan = new MacWindowsManager(); + #else + #ifdef WINDOWS + m_winsMan = new WinWindowsManager(); + #else + m_winsMan = new XWindowsManager(); + #endif + #endif + + updateTitle(); + + //initialise midi + m_midiStream=NULL; + Pm_Initialize(); + detectMidiDevices(); + Pt_Start(1, NULL, NULL); + + //initialize osc + try { + m_socket = new UdpListeningReceiveSocket( + IpEndpointName(IpEndpointName::ANY_ADDRESS, 8327), + this); + pthread_mutex_init(&m_zonesMutex, NULL); + pthread_create(&m_oscThread, NULL, oscThreadFunction , this); + } + catch(const exception& e) { + DEBUG("Error in the OscManager "<<e.what()); + } +} + +void Controlar::refreshWindowList() { + //recreate windows list + m_winsMan->updateWindowsList(); + + //clean all zones and reassign windows + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->refreshCutWin(); + } + + //rebuild zone menu + m_zoneMenu->clear(); + vector<CutWindow*>::iterator itWin = m_winsMan->editWindowList().begin(); + for(; itWin!=m_winsMan->editWindowList().end(); ++itWin) { + m_zoneMenu->addWithSub((string("Select/")+(*itWin)->getName()).c_str(), + statZoneSelect, (*itWin)); + } + m_zonePanel->setWindowList(m_winsMan->editWindowList()); + + + m_zoneMenu->addWithSub("Alpha/Opaque", statZoneAlpha, &m_alphaLevels[3]); + m_zoneMenu->addWithSub("Alpha/Mostly Opaque", + statZoneAlpha, &m_alphaLevels[2]); + m_zoneMenu->addWithSub("Alpha/Half Transparent", + statZoneAlpha, &m_alphaLevels[1]); + m_zoneMenu->addWithSub("Alpha/Mostly Transparent", + statZoneAlpha, &m_alphaLevels[0]); + m_zoneMenu->addWithSub("Transform/No Rotation", + statZoneTrans, &m_menuValues[ZoneWidget::NONE]); + m_zoneMenu->addWithSub("Transform/Rotate 90", + statZoneTrans, &m_menuValues[ZoneWidget::ROTATE_90]); + m_zoneMenu->addWithSub("Transform/Rotate 180", + statZoneTrans, &m_menuValues[ZoneWidget::ROTATE_180]); + m_zoneMenu->addWithSub("Transform/Rotate 270", + statZoneTrans, &m_menuValues[ZoneWidget::ROTATE_270]); + m_zoneMenu->addWithSub("Shape/Box", + statZoneShape, &m_menuValues[ZoneWidget::BOX]); + m_zoneMenu->addWithSub("Shape/Circle", + statZoneShape, &m_menuValues[ZoneWidget::CIRCLE]); + m_zoneMenu->addWithSub("Shape/Frame", + statZoneShape, &m_menuValues[ZoneWidget::FRAME]); + m_zoneMenu->addWithSub("Color/Normal", + statZoneCol, &m_menuValues[ZoneWidget::NORMAL]); + m_zoneMenu->addWithSub("Color/Inverse", + statZoneCol, &m_menuValues[ZoneWidget::INVERT]); + m_zoneMenu->addWithSub("Visible/All Scenes", + statZoneVisible, + &m_menuValues[ZoneWidget::VISIBLE_ALL]); + m_zoneMenu->addWithSub("Visible/Current Scene Only", + statZoneVisible, + &m_menuValues[ZoneWidget::VISIBLE_CURRENT]); + m_zoneMenu->addWithSub("Update/All Scenes", + statZoneUpdate, + &m_menuValues[ZoneWidget::UPDATE_ALL]); + m_zoneMenu->addWithSub("Update/Current Scene Only", + statZoneUpdate, + &m_menuValues[ZoneWidget::UPDATE_CURRENT]); + m_zoneMenu->addWithSub("Content/Global", + statZoneContent, + &m_menuValues[ZoneWidget::CONTENT_GLOBAL]); + m_zoneMenu->addWithSub("Content/Local", + statZoneContent, + &m_menuValues[ZoneWidget::CONTENT_LOCAL]); + m_zoneMenu->addWithSub("MIDI Input/Learn", + statZoneMidiLearn, this); + m_zoneMenu->addWithSub("MIDI Input/No effect", + statZoneMidiEffect, &m_menuValues[ZoneWidget::NO_EFFECT]); + m_zoneMenu->addWithSub("MIDI Input/Show", + statZoneMidiEffect, &m_menuValues[ZoneWidget::SHOW]); + m_zoneMenu->addWithSub("MIDI Input/Hide", + statZoneMidiEffect, &m_menuValues[ZoneWidget::HIDE]); + + m_zoneMenu->add("Clear", statZoneClear, this); + m_zoneMenu->add("Delete", statZoneDelete, this); + + m_mainMenu->hide(); + m_zoneMenu->hide(); +} + +void Controlar::update() { + Controlar::getInstance()->damage(FL_DAMAGE_USER1); + parseMidi(); +} + + +void Controlar::parseMidi() { + PmEvent buffer[10]; + vector<PortMidiStream*>::iterator itSt = m_midiStreamsVec.begin(); + for(; itSt!=m_midiStreamsVec.end();++itSt) { + int nb = Pm_Read((*itSt), buffer, 10); + for(int i=0;i<nb;++i) { + long status = Pm_MessageStatus(buffer[i].message); + int msgType = status >> 4; + int msgChannel = (status & 15) + 1; + int control = Pm_MessageData1(buffer[i].message); + int value = Pm_MessageData2(buffer[i].message); + if(m_midiLearning) { + m_midiType = msgType; + m_midiChannel = msgChannel; + m_midiControl = control; + m_midiLearning=false; + } + if(msgType==m_midiType + && msgChannel==m_midiChannel + && control==m_midiControl) { + setScene(value); + } + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->processMidi(msgType, msgChannel, + control, value); + } + } + } +} + +void Controlar::openMidiDevice(const std::string& devName) { + map<string, int>::iterator itDev=m_midiDevMap.find(devName); + if(itDev!=m_midiDevMap.end()) { + if(m_midiStreamsMap.find(devName)!=m_midiStreamsMap.end()) { + Pm_Close(m_midiStreamsMap[devName]); + } + m_midiStreamsMap[devName]=NULL; + Pm_OpenInput(&m_midiStreamsMap[devName], + itDev->second, NULL, 100, NULL, NULL); + } + refreshMidiStreams(); +} + +void Controlar::closeMidiDevice(const std::string& devName) { + map<string, PortMidiStream*>::iterator itSt=m_midiStreamsMap.find(devName); + if(itSt!=m_midiStreamsMap.end()) { + Pm_Close(itSt->second); + m_midiStreamsMap.erase(devName); + } + refreshMidiStreams(); +} + +void Controlar::refreshMidiStreams() { + m_midiStreamsVec.clear(); + map<string, PortMidiStream*>::iterator itSt=m_midiStreamsMap.begin(); + for(; itSt!=m_midiStreamsMap.end(); ++itSt) { + m_midiStreamsVec.push_back(itSt->second); + } +} + +void Controlar::detectMidiDevices() { + + Pm_Terminate(); + Pm_Initialize(); + int nbDevices = Pm_CountDevices(); +/* + FlipMenu* midiMenu = m_mainMenu->getSub("Midi Input"); + midiMenu->clear(); + m_midiDevices.clear(); + m_midiDeviceNames.clear(); + m_midiStreamsMap.clear(); +*/ + vector<bool> devOps; + vector<string> devNames; + for(int i=0; i<nbDevices; ++i) { + const PmDeviceInfo* info = Pm_GetDeviceInfo(i); + if(info->input>0) { + string devName = string(info->name); +/* + m_midiDevices.push_back(i); + m_midiDeviceNames.push_back(devName); + midiMenu->add(devName, statMidiDev, &m_midiDevices.back()); +*/ + m_midiDevMap[devName]=i; + devNames.push_back(devName); + if(m_midiStreamsMap.find(devName)!=m_midiStreamsMap.end()) { + openMidiDevice(devName); + devOps.push_back(true); + } + else { + devOps.push_back(false); + } + } + } + m_mainPanel->setMidiDevices(devNames, devOps); +/* + if(m_currentMidiDeviceName.compare("")!=0) { + setMidiDeviceFromName(m_currentMidiDeviceName); + } +*/ +} + +ZoneWidget* Controlar::startDuplicating(ZoneWidget* dupWid) { + m_duplicatedZone = new ZoneWidget(); + m_duplicatedZone->copy(dupWid); + addZone(m_duplicatedZone); + m_duplicatedZone->forceDraggingZone(); + return m_duplicatedZone; +} + +int Controlar::handle(int event) { + int res=0; + if(!m_drawing) { + res=Fl_Group::handle(event); + } + if(res>0) { + redraw(); + } + else { + switch(event) { + case FL_KEYDOWN: { + if(Fl::event_command()) { + switch(Fl::event_key()) { + case 'o': cbOpen(); break; + case 's': cbSave(); break; + case 'f': cbFullscreen(); break; + case 'm': cbFlip(); break; + case 'q': cbQuit(); break; + default:break; + } + res=1; + } + }break; + case FL_PUSH: + if(m_duplicating) { + m_duplicating=false; + } + switch(Fl::event_button()) { + case FL_LEFT_MOUSE: { + m_drawStartX=Fl::event_x(); + m_drawStartY=Fl::event_y(); + m_drawCurX=Fl::event_x(); + m_drawCurY=Fl::event_y(); + m_drawing=true; + m_drawingGroup=false; + if(Fl::event_shift()) { + m_drawingGroup=true; + } + m_mainMenu->hide(); + m_zoneMenu->hide(); + m_mainPanel->hide(); + m_zonePanel->hide(); + redraw(); + }break; + case FL_RIGHT_MOUSE: { + detectMidiDevices(); + insert(*m_mainMenu, children()); + int menPosX = (Fl::event_x()>w()-m_mainPanel->w())? + Fl::event_x()-m_mainPanel->w():Fl::event_x(); + int menPosY = (Fl::event_y()>h()-m_mainPanel->h())? + Fl::event_y()-m_mainPanel->h():Fl::event_y(); + //m_mainMenu->position(menPosX, menPosY); + //m_mainMenu->show(); + //m_zoneMenu->hide(); + m_mainPanel->position(menPosX, menPosY); + insert(*m_mainPanel,children()); + m_mainPanel->show(); + }break; + default:break; + } + res=1; + break; + case FL_DRAG: + m_drawCurX=Fl::event_x(); + m_drawCurY=Fl::event_y(); + break; + case FL_RELEASE: + switch(Fl::event_button()) { + case FL_LEFT_MOUSE: { + int minPixels=20; + int drawStopX = Fl::event_x(); + int drawStopY = Fl::event_y(); + if(m_drawing) { + if(abs(m_drawStartX-drawStopX)>minPixels && + abs(m_drawStartY-drawStopY)>minPixels) { + if(m_drawingGroup) { + addGroup(new GroupWidget(m_drawStartX, + m_drawStartY, + abs(m_drawStartX-drawStopX), + abs(m_drawStartY-drawStopY))); + } + else { + addZone(new ZoneWidget( + m_drawStartX-ZoneWidget::OUTER, + m_drawStartY-ZoneWidget::OUTER, + abs(m_drawStartX-drawStopX) + +ZoneWidget::OUTER*2, + abs(m_drawStartY-drawStopY) + +ZoneWidget::OUTER*2)); + } + } + } + m_drawing=false; + res=1; + }break; + default:break; + } + break; + default:break; + } + } + redraw(); + return res; +} + +void Controlar::ProcessMessage(const osc::ReceivedMessage& mess, + const IpEndpointName& remoteEndpoint) { + try { + string name = string(mess.AddressPattern()); + if(name.find("/controllar/")!=string::npos) { + if(name.find("/scene")!=string::npos && mess.ArgumentCount()>0) { + pthread_mutex_lock(&m_zonesMutex); + m_askedScene=mess.ArgumentsBegin()->AsInt32(); + pthread_mutex_unlock(&m_zonesMutex); + } + } + } + catch(const exception& e) { + cout<<"Exception when receiving osc message "<<e.what()<<endl; + } +} + + +void Controlar::cbZone(Fl_Widget* wid) { + refreshWindowList(); + + m_clickedZone = static_cast<ZoneWidget*>(wid); + int menPosX = (Fl::event_x()>w()-m_zonePanel->w())? + Fl::event_x()-m_zonePanel->w():Fl::event_x(); + int menPosY = (Fl::event_y()>h()-m_zonePanel->h())? + Fl::event_y()-m_zonePanel->h():Fl::event_y(); + //m_zoneMenu->position(menPosX, menPosY); + //insert(*m_zoneMenu, children()); + //m_zoneMenu->show(); + m_zonePanel->setZone(m_clickedZone); + m_zonePanel->position(menPosX, menPosY); + m_zonePanel->refresh(); + insert(*m_zonePanel,children()); + m_zonePanel->show(); +} + +void Controlar::cbSceneNext() { + if(m_scenes.find(m_currentScene+1)==m_scenes.end()) { + m_scenes[m_currentScene+1]="scene"; + } + setScene(m_currentScene+1); +} + +void Controlar::cbScenePrev() { + setScene(m_currentScene-1); +} + +/* +void Controlar::cbSceneDuplicate() { + //save ref to current set + int cur=m_currentSet; + + //create new set and make it current + m_zonesSets.push_back(map<unsigned int, ZoneWidget*>()); + setSet(m_zonesSets.size()-1); + + //copy zones + map<unsigned int, ZoneWidget*>::iterator itZo = m_zonesSets[cur].begin(); + for(; itZo!=m_zonesSets[cur].end(); ++itZo) { + ZoneWidget* newZo = new ZoneWidget(); + newZo->copy(itZo->second); + addZone(newZo); + } + setSet(m_currentSet); +} +*/ + +void Controlar::cbSceneDelete() { +/* + if(m_zonesSets.size()>1) { + if(fl_choice("Are you sure you want to delete the current set ?", + "No", "Yes", "")) { + map<unsigned int, ZoneWidget*>::iterator itZo + = m_zonesSets[m_currentSet].begin(); + for(; itZo!=m_zonesSets[m_currentSet].end(); ++itZo) { + Fl::delete_widget(itZo->second); + } + m_zonesSets.erase(m_zonesSets.begin()+m_currentSet); + setSet(0); + } + } +*/ +} + +void Controlar::setScene(const int& scene) { + if(m_scenes.find(scene)!=m_scenes.end()) { + //store images for zones that only update on current scene + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->setScene(scene); + } + //change scene + m_currentScene=scene; + m_mainPanel->setCurrentScene(scene); + m_mainPanel->refresh(); + updateTitle(); + } + m_mainMenu->hide(); +} + +void Controlar::updateTitle() { + ostringstream oss; + oss<<m_currentScene; + m_title = "ControllAR - "+m_fileName+" - Scene "+oss.str(); + label(m_title.c_str()); +} + +void Controlar::cbSceneLearn() { + m_midiLearning=true; + m_mainMenu->hide(); +} + +void Controlar::cbFlip() { + m_flipped=!m_flipped; + resize(x(),y(),w(),h()); + m_mainMenu->hide(); + map<unsigned int, ZoneWidget*>::iterator itZo=m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->recomputePixels(); + } +} + +void Controlar::cbFullscreen() { + m_fullscreen=!m_fullscreen; + if(m_fullscreen) { + fullscreen(); + } + else { + fullscreen_off(); + } + m_mainMenu->hide(); + fl_cursor(FL_CURSOR_NONE); +} + +#ifdef GL +void Controlar::initGL() { + #ifdef OSX + glewExperimental = GL_TRUE; + #endif + glewInit(); + glGenVertexArrays(1, &m_vertexArrayID); + glBindVertexArray(m_vertexArrayID); + glGenBuffers(1, &m_vertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); + m_vertexBufferData[0]=0;m_vertexBufferData[1]=0;m_vertexBufferData[2]=0; + m_vertexBufferData[3]=1;m_vertexBufferData[4]=0;m_vertexBufferData[5]=0; + m_vertexBufferData[6]=1;m_vertexBufferData[7]=1;m_vertexBufferData[8]=0; + m_vertexBufferData[9]=0;m_vertexBufferData[10]=0;m_vertexBufferData[11]=0; + m_vertexBufferData[12]=1;m_vertexBufferData[13]=1;m_vertexBufferData[14]=0; + m_vertexBufferData[15]=0;m_vertexBufferData[16]=1;m_vertexBufferData[17]=0; + glBufferData(GL_ARRAY_BUFFER, sizeof(m_vertexBufferData), + m_vertexBufferData, GL_STATIC_DRAW); + + GLint compile_ok = GL_FALSE, link_ok = GL_FALSE; + + GLuint vertexShader, fragmentShader; + + //ZONE PROGRAM + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *vs_source = + "#version 110\n" + "attribute vec3 coord;\n" + "uniform vec2 winSize;\n" + "uniform vec2 zonePos;\n" + "uniform vec2 zoneSize;\n" + "uniform vec2 cutPos;\n" + "uniform vec2 cutSize;\n" + "uniform float mirrored;\n" + "uniform float outer;\n" + "varying vec2 pixRatio;\n" + "varying vec4 pixToSides;\n" //T,B,L,R + "void main(void) {\n" + " vec2 zonePixRatio;\n" + " if(mirrored>0.0) {\n" + " zonePixRatio = vec2(coord.x, coord.y);\n" + " pixRatio = vec2(\n" + " (coord.x*zoneSize.x-outer)/cutSize.x,\n" + " (coord.y*zoneSize.y-outer)/cutSize.y);\n" + " }\n" + " else {\n" + " zonePixRatio = vec2(coord.x, 1.0-coord.y);\n" + " pixRatio = vec2(\n" + " (coord.x*zoneSize.x-outer)/cutSize.x,\n" + " ((1.0-coord.y)*zoneSize.y-outer)/cutSize.y);\n" + " }\n" + " vec2 cutPixPos = pixRatio*cutSize;\n" + " vec2 pos = ((cutPixPos+cutPos)/winSize)*2.0-1.0;\n" + " if(mirrored>0.0) {\n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + " }\n" + " else {\n" + " gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n" + " }\n" + " vec2 zonePixPos = zonePixRatio*zoneSize;\n" + " pixToSides = vec4(zonePixPos.y,\n" + " zoneSize.y-zonePixPos.y,\n" + " zonePixPos.x,\n" + " zoneSize.x-zonePixPos.x);\n" + "}\n"; + glShaderSource(vertexShader, 1, &vs_source, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in vertex shader" << endl; + GLint maxLength = 0; + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<GLchar> errorLog(maxLength); + glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &errorLog[0]); + vector<GLchar>::iterator itC=errorLog.begin(); + for(; itC!=errorLog.end(); ++itC) { + cout<<*itC; + } + cout<<endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *fs_source = + "#version 110\n" + "varying vec2 pixRatio;\n" + "varying vec4 pixToSides;\n" //T,B,L,R + "uniform sampler2D windowTexture;\n" + "uniform sampler2D coordsTexture;\n" + "uniform vec2 winSize;\n" + "uniform vec2 zonePos;\n" + "uniform vec2 zoneSize;\n" + "uniform vec2 cutPos;\n" + "uniform vec2 cutSize;\n" + "uniform float mirrored;\n" + "uniform int state;\n"//NORMAL,MOVE_CUT,RESIZE_CUT,MOVE_ZONE,RESIZE_ZONE + "uniform float alpha;\n" + "uniform float filled;\n" + "uniform float invert;\n" + "uniform float outer;\n" + "uniform float inner;\n" + "void main(void) { \n" + " float thick=1.0;\n" + " if(filled>0.0) {\n" + " if(texture2D(coordsTexture, pixRatio).r>=0.0 \n" + " && pixRatio.x>0.0 && pixRatio.x<1.0 \n" + " && pixRatio.y>0.0 && pixRatio.y<1.0) {\n" + " vec4 winCol = texture2D(windowTexture, \n" + " texture2D(coordsTexture, pixRatio).rg);\n" +#ifdef WINDOWS + " winCol = texture2D(windowTexture, pixRatio);\n" +#else + " winCol = vec4(winCol.b,winCol.g,winCol.r,winCol.a);\n" +#endif + " if(invert>0.0) {\n" + " gl_FragColor=vec4(1.0-winCol.r, 1.0-winCol.g, \n" + " 1.0-winCol.b, alpha);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(winCol.rgb, alpha);\n" + " }\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(0.0, 0.0, 0.0, 0.0);\n" + " }\n" + " if(state>0) {\n" + //outer border + " if(pixToSides.x<thick || pixToSides.y<thick \n" + " || pixToSides.z<thick || pixToSides.w<thick) { \n" + " if(state==3) {\n" + " gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n" + " }\n" + " }\n" + //inner border + " if(((abs(pixToSides.x-inner)<thick \n" + " || abs(pixToSides.y-inner)<thick)\n" + " && (pixToSides.z>inner && pixToSides.w>inner))\n" + " || (abs(pixToSides.z-inner)<thick \n" + " || abs(pixToSides.w-inner)<thick)\n" + " && (pixToSides.x>inner && pixToSides.y>inner)) {\n" + " if(state==1) {\n" + " gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n" + " }\n" + " }\n" + //outer corner + " if(abs(sqrt(pow(pixToSides.w, 2.0)\n" + " +pow(pixToSides.y, 2.0))-inner)<thick) {\n" + " if(state==4) {\n" + " gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n" + " }\n" + " }\n" + //inner corner + " if(( abs(sqrt(pow(pixToSides.w, 2.0)\n" + " +pow(pixToSides.y, 2.0))-inner*3.0)<thick)\n" + " && pixToSides.x>inner && pixToSides.y>inner \n" + " && pixToSides.z>inner && pixToSides.w>inner ){\n" + " if(state==2) {\n" + " gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n" + " }\n" + " }\n" + " }\n" + " }\n" + " else {\n" + " if(pixToSides.x<thick || pixToSides.y<thick \n" + " || pixToSides.z<thick || pixToSides.w<thick) { \n" + " if(state>0) {\n" + " gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor=vec4(1.0,1.0,1.0,1.0);\n" + " }\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n" + " }\n" + " }\n" + "}\n"; + glShaderSource(fragmentShader, 1, &fs_source, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in fragment shader" << endl; + GLint maxLength = 0; + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<GLchar> errorLog(maxLength); + glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &errorLog[0]); + vector<GLchar>::iterator itC=errorLog.begin(); + for(; itC!=errorLog.end(); ++itC) { + cout<<*itC; + } + cout<<endl; + } + m_zoneProgram = glCreateProgram(); + glAttachShader(m_zoneProgram, vertexShader); + glAttachShader(m_zoneProgram, fragmentShader); + glLinkProgram(m_zoneProgram); + glGetProgramiv(m_zoneProgram, GL_LINK_STATUS, &link_ok); + if (!link_ok) { + cout << "Error in glLinkProgram" << endl; + } + m_zoneWinSizeUniform = glGetUniformLocation(m_zoneProgram, "winSize"); + m_zoneSizeUniform = glGetUniformLocation(m_zoneProgram, "zoneSize"); + m_zonePosUniform = glGetUniformLocation(m_zoneProgram, "zonePos"); + m_cutSizeUniform = glGetUniformLocation(m_zoneProgram, "cutSize"); + m_cutPosUniform = glGetUniformLocation(m_zoneProgram, "cutPos"); + m_zoneMirrorUniform = glGetUniformLocation(m_zoneProgram, "mirrored"); + m_zoneStateUniform = glGetUniformLocation(m_zoneProgram, "state"); + m_zoneAlphaUniform = glGetUniformLocation(m_zoneProgram, "alpha"); + m_zoneInvertUniform = glGetUniformLocation(m_zoneProgram, "invert"); + m_zoneFilledUniform = glGetUniformLocation(m_zoneProgram, "filled"); + m_zoneWindowUniform = glGetUniformLocation(m_zoneProgram, "windowTexture"); + m_zoneCoordsUniform = glGetUniformLocation(m_zoneProgram, "coordsTexture"); + m_zoneInnerUniform = glGetUniformLocation(m_zoneProgram, "inner"); + m_zoneOuterUniform = glGetUniformLocation(m_zoneProgram, "outer"); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &m_firstTextureID); + glBindTexture(GL_TEXTURE_2D, m_firstTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glActiveTexture(GL_TEXTURE1); + glGenTextures(1, &m_secondTextureID); + glBindTexture(GL_TEXTURE_2D, m_secondTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + //GROUP PROGRAM + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *vs_source = + "#version 110\n" + "attribute vec3 coord;\n" + "uniform vec2 winSize;\n" + "uniform vec2 groupPos;\n" + "uniform vec2 groupSize;\n" + "uniform float mirrored;\n" + "varying vec2 pixPos;\n" + "void main(void) {\n" + " if(mirrored>0.0) {\n" + " pixPos = vec2(coord.x, coord.y);\n" + " }\n" + " else {\n" + " pixPos = vec2(coord.x, 1.0-coord.y);\n" + " }\n" + " vec2 pos = ((pixPos*groupSize+groupPos)/winSize)*2.0-1.0;\n" + " if(mirrored>0.0) {\n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + " }\n" + " else {\n" + " gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n" + " }\n" + "}\n"; + glShaderSource(vertexShader, 1, &vs_source, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in vertex shader" << endl; + GLint maxLength = 0; + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<GLchar> errorLog(maxLength); + glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &errorLog[0]); + vector<GLchar>::iterator itC=errorLog.begin(); + for(; itC!=errorLog.end(); ++itC) { + cout<<*itC; + } + cout<<endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *fs_source = + "#version 110\n" + "varying vec2 pixPos;\n" + "uniform vec2 winSize;\n" + "uniform float mirrored;\n" + "uniform float editing;\n" + "uniform float highlight;\n" + "void main(void) { \n" + " if(editing>0.0) {\n" + " gl_FragColor = vec4(0.0,1.0,0.0,1.0);\n" + " }\n" + " else {\n" + " if(pixPos.x<0.01 || pixPos.x>0.99 \n" + " || pixPos.y<0.01 || pixPos.y>0.99) { \n" + " gl_FragColor=vec4(0.0,0.5+highlight*0.5,0.0,1.0);\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\n" + " }\n" + " }\n" + "}\n"; + glShaderSource(fragmentShader, 1, &fs_source, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in fragment shader" << endl; + GLint maxLength = 0; + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<GLchar> errorLog(maxLength); + glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &errorLog[0]); + vector<GLchar>::iterator itC=errorLog.begin(); + for(; itC!=errorLog.end(); ++itC) { + cout<<*itC; + } + cout<<endl; + } + m_groupProgram = glCreateProgram(); + glAttachShader(m_groupProgram, vertexShader); + glAttachShader(m_groupProgram, fragmentShader); + glLinkProgram(m_groupProgram); + glGetProgramiv(m_groupProgram, GL_LINK_STATUS, &link_ok); + if (!link_ok) { + cout << "Error in glLinkProgram" << endl; + } + m_groupWinSizeUniform = glGetUniformLocation(m_groupProgram, "winSize"); + m_groupSizeUniform = glGetUniformLocation(m_groupProgram, "groupSize"); + m_groupPosUniform = glGetUniformLocation(m_groupProgram, "groupPos"); + m_groupMirrorUniform = glGetUniformLocation(m_groupProgram, "mirrored"); + m_groupEditingUniform = glGetUniformLocation(m_groupProgram, "editing"); + m_groupHighlightUniform = glGetUniformLocation(m_groupProgram, "highlight"); + } + + //CURSOR PROGRAM + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *vs_source = + "#version 110\n" + "attribute vec3 coord;\n" + "uniform vec2 winSize;\n" + "uniform vec2 cursPos;\n" + "uniform float mirrored;\n" + "varying vec2 pixPos;\n" + "void main(void) {\n" + " if(mirrored>0.0) {\n" + " pixPos = vec2(coord.x, coord.y);\n" + " }\n" + " else {\n" + " pixPos = vec2(coord.x, 1.0-coord.y);\n" + " }\n" + " vec2 pos = ((pixPos*vec2(10,10)+cursPos)/winSize)*2.0-1.0;\n" + " if(mirrored>0.0) {\n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + " }\n" + " else {\n" + " gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n" + " }\n" + "}\n"; + glShaderSource(vertexShader, 1, &vs_source, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in cursor vertex shader" << endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *fs_source = + "#version 110\n" + "uniform vec2 winSize;\n" + "uniform vec2 cursPos;\n" + "uniform float mirrored;\n" + "varying vec2 pixPos;\n" + "void main(void) {\n" + " if(pixPos.x<0.1 || pixPos.x>0.9 \n" + " || pixPos.y<0.1 || pixPos.y>0.9) { \n" + " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + " }\n" + " else {\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n" + " }\n" + "}\n"; + glShaderSource(fragmentShader, 1, &fs_source, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in cursor fragment shader" << endl; + } + m_cursorProgram = glCreateProgram(); + glAttachShader(m_cursorProgram, vertexShader); + glAttachShader(m_cursorProgram, fragmentShader); + glLinkProgram(m_cursorProgram); + glGetProgramiv(m_cursorProgram, GL_LINK_STATUS, &link_ok); + if (!link_ok) { + cout << "Error in cursor glLinkProgram" << endl; + } + m_cursorWinSizeUniform = glGetUniformLocation(m_cursorProgram, "winSize"); + m_cursorPosUniform = glGetUniformLocation(m_cursorProgram, "cursPos"); + m_cursorMirrorUniform = glGetUniformLocation(m_cursorProgram, "mirrored"); + } + + + //DRAW PROGRAM + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *vs_source = + "#version 110\n" + "attribute vec3 coord;\n" + "uniform vec2 winSize;\n" + "uniform vec2 drawPos;\n" + "uniform vec2 drawSize;\n" + "uniform float mirrored;\n" + "void main(void) {\n" + " vec2 pixPos;\n" + " if(mirrored>0.0) {\n" + " pixPos = vec2(coord.x, coord.y);\n" + " }\n" + " else {\n" + " pixPos = vec2(coord.x, 1.0-coord.y);\n" + " }\n" + " vec2 pos = ((pixPos*drawSize+drawPos)/winSize)*2.0-1.0;\n" + " if(mirrored>0.0) {\n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + " }\n" + " else {\n" + " gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n" + " }\n" + "}\n"; + glShaderSource(vertexShader, 1, &vs_source, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in draw vertex shader" << endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *fs_source = + "#version 110\n" + "uniform vec2 winSize;\n" + "uniform vec2 cursPos;\n" + "uniform float mirrored;\n" + "uniform float drawingGroup;\n" + "void main(void) {\n" + " gl_FragColor = vec4(1.0-drawingGroup, drawingGroup, 0.0, 1.0);\n" + "}\n"; + glShaderSource(fragmentShader, 1, &fs_source, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in draw fragment shader" << endl; + } + m_drawingProgram = glCreateProgram(); + glAttachShader(m_drawingProgram, vertexShader); + glAttachShader(m_drawingProgram, fragmentShader); + glLinkProgram(m_drawingProgram); + glGetProgramiv(m_drawingProgram, GL_LINK_STATUS, &link_ok); + if (!link_ok) { + cout << "Error in cursor glLinkProgram" << endl; + } + m_drawWinSizeUniform = glGetUniformLocation(m_drawingProgram, "winSize"); + m_drawPosUniform = glGetUniformLocation(m_drawingProgram, "drawPos"); + m_drawSizeUniform = glGetUniformLocation(m_drawingProgram, "drawSize"); + m_drawMirrorUniform = glGetUniformLocation(m_drawingProgram, "mirrored"); + m_drawGroupUniform = glGetUniformLocation(m_drawingProgram, "drawingGroup"); + } + + //MENU PROGRAM + { + vertexShader = glCreateShader(GL_VERTEX_SHADER); + const char *vs_source = + "#version 110\n" + "attribute vec3 coord;\n" + "uniform vec2 winSize;\n" + "uniform vec2 menuPos;\n" + "uniform vec2 menuSize;\n" + "uniform float mirrored;\n" + "varying vec2 pixPos;\n" + "void main(void) {\n" + " if(mirrored>0.0) {\n" + " pixPos = vec2(coord.x, coord.y);\n" + " }\n" + " else {\n" + " pixPos = vec2(coord.x, 1.0-coord.y);\n" + " }\n" + " vec2 pos = ((pixPos*menuSize+menuPos)/winSize)*2.0-1.0;\n" + " if(mirrored>0.0) {\n" + " gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);\n" + " }\n" + " else {\n" + " gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);\n" + " }\n" + "}\n"; + glShaderSource(vertexShader, 1, &vs_source, NULL); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in menu vertex shader" << endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + const char *fs_source = + "#version 110\n" + "varying vec2 pixPos;\n" + "uniform float mirrored;\n" + "uniform sampler2D menuTexture;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(menuTexture, pixPos);\n" + "}\n"; + glShaderSource(fragmentShader, 1, &fs_source, NULL); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compile_ok); + if (!compile_ok) { + cout << "Error in menu fragment shader" << endl; + } + m_menuProgram = glCreateProgram(); + glAttachShader(m_menuProgram, vertexShader); + glAttachShader(m_menuProgram, fragmentShader); + glLinkProgram(m_menuProgram); + glGetProgramiv(m_menuProgram, GL_LINK_STATUS, &link_ok); + if (!link_ok) { + cout << "Error in cursor glLinkProgram" << endl; + } + m_menuWinSizeUniform = glGetUniformLocation(m_menuProgram, "winSize"); + m_menuPosUniform = glGetUniformLocation(m_menuProgram, "menuPos"); + m_menuSizeUniform = glGetUniformLocation(m_menuProgram, "menuSize"); + m_menuMirrorUniform = glGetUniformLocation(m_menuProgram, "mirrored"); + m_menuTextureUniform = glGetUniformLocation(m_menuProgram, "menuTexture"); + } + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &m_firstTextureID); + glBindTexture(GL_TEXTURE_2D, m_firstTextureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} +#endif + +void Controlar::draw() { + + + +/* + pthread_mutex_lock(&m_zonesMutex); + if(m_askedScene>-1) { + setScene(m_askedScene); + m_askedScene=-1; + } + pthread_mutex_unlock(&m_zonesMutex); +*/ +#ifdef GL + if(!valid()) { + initGL(); + } + + glViewport(0, 0, w(), h()); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + //reset each window + vector<CutWindow*>::iterator itWin = m_winsMan->editWindowList().begin(); + for(; itWin!=m_winsMan->editWindowList().end(); ++itWin) { + (*itWin)->releaseImage(); + } + + //draw groups + glUseProgram(m_groupProgram); + glUniform1f(m_groupMirrorUniform, m_flipped); + glUniform2f(m_groupWinSizeUniform, w(), h()); + map<unsigned int, GroupWidget*>::iterator itGr = m_groups.begin(); + for(; itGr!=m_groups.end(); ++itGr) { + itGr->second->draw(); + } + + //draw zones + m_currentWindowName=""; + glUseProgram(m_zoneProgram); + glUniform1f(m_zoneMirrorUniform, m_flipped); + glUniform2f(m_zoneWinSizeUniform, w(), h()); + glUniform1i(m_zoneWindowUniform, 0); + glUniform1i(m_zoneCoordsUniform, 1); + glUniform1f(m_zoneInnerUniform, ZoneWidget::INNER); + glUniform1f(m_zoneOuterUniform, ZoneWidget::OUTER); + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->drawZone(m_currentScene); + } + + //draw menus + glUseProgram(m_menuProgram); + glUniform1f(m_menuMirrorUniform, m_flipped); + glUniform2f(m_menuWinSizeUniform, w(), h()); + glUniform1i(m_menuTextureUniform, 0); + if(m_mainMenu->visible()) { + m_mainMenu->draw(); + } + else if(m_zoneMenu->visible()) { + m_zoneMenu->draw(); + } + if(m_mainPanel->visible()) { + m_mainPanel->draw(); + } + if(m_zonePanel->visible()) { + m_zonePanel->draw(); + } + //draw drawing + if(m_drawing) { + glUseProgram(m_drawingProgram); + glUniform1f(m_drawMirrorUniform, m_flipped); + glUniform2f(m_drawWinSizeUniform, w(), h()); + glUniform2f(m_drawSizeUniform, m_drawCurX-m_drawStartX, + m_drawCurY-m_drawStartY); + glUniform2f(m_drawPosUniform, m_drawStartX, + m_drawStartY); + glUniform1f(m_drawGroupUniform, m_drawingGroup); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + +#ifndef LINUX + //draw cursor + glUseProgram(m_cursorProgram); + glUniform1f(m_cursorMirrorUniform, m_flipped); + glUniform2f(m_cursorWinSizeUniform, w(), h()); + glUniform2f(m_cursorPosUniform, Fl::event_x_root()-x(), + Fl::event_y_root()-y()); + glDrawArrays(GL_TRIANGLES, 3, 3); +#endif + + glDisableVertexAttribArray(0); + +#else + //redraw whole background only when needed + if(damage()&FL_DAMAGE_ALL) { + fl_draw_box(box(), 0, 0, w(), h(), FL_BLACK); + map<unsigned int, ZoneWidget*>::iterator itZo + = m_zonesSets[m_currentSet].begin(); + for(; itZo!=m_zonesSets[m_currentSet].end(); ++itZo) { + itZo->second->setRateReached(); + } + } + else { //redraw only dynamic zones background and mask prev curs + fl_rect(m_cursPosX-5, + m_cursPosY-5, + 10, 10, FL_BLACK); + map<unsigned int, ZoneWidget*>::iterator itZo + = m_zonesSets[m_currentSet].begin(); + for(; itZo!=m_zonesSets[m_currentSet].end(); ++itZo) { + if(itZo->second->getRate()==ZoneWidget::DYNAMIC) { + ZoneWidget* wid = itZo->second; + fl_rectf(wid->x(), + m_flipped?h()-wid->y()-wid->h():wid->y(), + wid->w(), + wid->h(), + FL_BLACK); + } + } + } + + //reset windows + vector<CutWindow*>::iterator itWin = m_winsMan->editWindowList().begin(); + for(; itWin!=m_winsMan->editWindowList().end(); ++itWin) { + (*itWin)->releaseImage(); + } + + //draw groups + map<unsigned int, GroupWidget*>::iterator itGr = m_groups.begin(); + for(; itGr!=m_groups.end(); ++itGr) { + itGr->second->draw(); + } + + + //draw zones + map<unsigned int, ZoneWidget*>::iterator itZo + = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->draw(); + } + + //draw menus + if(m_mainMenu->visible()) { + m_mainMenu->draw(); + } + else if(m_zoneMenu->visible()) { + m_zoneMenu->draw(); + } + if(m_mainBar->visible()) { + m_mainBar->draw(); + } + if(m_zoneBar->visible()) { + m_zoneBar->draw(); + } + //draw zone creation rectangles + if(m_drawing) { + fl_rectf(m_drawStartX, + m_flipped?h()-m_drawStartY-(m_drawCurY-m_drawStartY):m_drawStartY, + m_drawCurX-m_drawStartX, + m_drawCurY-m_drawStartY, + m_drawingGroup?FL_GREEN:FL_RED); + } + + //draw cursor replacement + m_cursPosX = Fl::event_x_root()-x(); + m_cursPosY=m_flipped?h()-(Fl::event_y_root()-y()):Fl::event_y_root()-y(); + fl_rectf(m_cursPosX-5, + m_cursPosY-5, + 10, 10, FL_BLACK); + fl_rect(m_cursPosX-5, + m_cursPosY-5, + 10, 10, FL_WHITE); +#endif + +} + +void Controlar::resize(int x, int y, int w, int h) { + #ifdef GL + Fl_Gl_Window::resize(x,y,w,h); + #else + Fl_Double_Window::resize(x,y,w,h); + #endif +} + +CutWindow* Controlar::getWindow(const string& name) { + return m_winsMan->getWindow(name); +} + +CutWindow* Controlar::getWindow(const int& win) { + return m_winsMan->editWindowList()[win]; +} + +void Controlar::cbOpen() { + Fl_Native_File_Chooser fnfc; + fnfc.title("Open ControlAR File"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_FILE); + if(fnfc.show()==0) { + load(fnfc.filename()); + } + m_mainMenu->hide(); +} + + +void Controlar::cbSaveAs() { + Fl_Native_File_Chooser fnfc; + fnfc.title("Save ControlAR File"); + fnfc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE); + if(fnfc.show()==0) { + save(fnfc.filename()); + } + m_mainMenu->hide(); +} + +void Controlar::cbSave() { + if(m_fileName.compare("")!=0) { + save(m_fileName); + } + else { + cbSaveAs(); + } + m_mainMenu->hide(); +} + +void Controlar::load(const std::string& fileName) { + + DEBUG("Opening "<<fileName); + bool open=true; + xmlDocPtr doc = xmlReadFile(fileName.c_str(), NULL, 0); + xmlNodePtr rootNode = xmlDocGetRootElement(doc); + if(doc==NULL || rootNode==NULL) { + DEBUG("Could not open file "<<fileName); + open=false; + } + else if(xmlStrcmp(rootNode->name, (const xmlChar*)"ControllAR")) { + xmlFreeDoc(doc); + DEBUG("Could not open file "<<fileName); + open=false; + } + if(open) { //if the file is valid + m_fileName=fileName; + + //delete all widgets + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + Fl::delete_widget(itZo->second); + } + m_zones.clear(); + m_scenes.clear(); + map<unsigned int, GroupWidget*>::iterator itGr = m_groups.begin(); + for(; itGr!=m_groups.end(); ++itGr) { + Fl::delete_widget(itGr->second); + } + m_groups.clear(); + + m_winsMan->updateWindowsList(); + + //load/create all zone sets and retrieve midi parameters + m_currentScene=0; + xmlNodePtr sNode; + for(sNode=rootNode->children; sNode; sNode=sNode->next) { + if(sNode->type == XML_ELEMENT_NODE) { + if(!xmlStrcmp(sNode->name, (const xmlChar *)"Group")) { + GroupWidget* wid = new GroupWidget(); + addGroup(wid); + wid->load(sNode); + } + else if(!xmlStrcmp(sNode->name, (const xmlChar *)"Scene")) { + string sceneStr; + char* value = NULL; + value = (char*)xmlGetProp(sNode, (xmlChar*)"name"); + if(value!=NULL) { + sceneStr=string(value); + } + m_scenes[m_currentScene]=sceneStr; + m_currentScene++; + } + else if (!xmlStrcmp(sNode->name, (const xmlChar *)"Zone")) { + ZoneWidget* wid = new ZoneWidget(); + addZone(wid); + wid->load(sNode); + } + else if(!xmlStrcmp(sNode->name, (const xmlChar *)"Midi")) { + char* value = NULL; + value = (char*)xmlGetProp(sNode,(xmlChar*)"device"); + if(value!=NULL) { + setMidiDeviceFromName(string(value)); + } + value = (char*)xmlGetProp(sNode,(xmlChar*)"set_channel"); + if(value!=NULL) { + m_midiChannel=atoi(value); + } + value = (char*)xmlGetProp(sNode,(xmlChar*)"set_type"); + if(value!=NULL) { + m_midiType=atoi(value); + } + value = (char*)xmlGetProp(sNode,(xmlChar*)"set_control"); + if(value!=NULL) { + m_midiControl=atoi(value); + } + } + } + } + + //set current scene to 0 + m_currentScene=0; + setScene(0); + DEBUG("Opened file "<<fileName); + } + updateTitle(); + m_mainPanel->hide(); +} + +void Controlar::save(const std::string& fileName) { + DEBUG("Saving to "<<fileName); + xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); + xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "ControllAR"); + + //save groups + map<unsigned int, GroupWidget*>::iterator itGr = m_groups.begin(); + for(; itGr!=m_groups.end(); ++itGr) { + itGr->second->save(rootNode); + } + + //save scenes + map<int, string>::iterator itSc = m_scenes.begin(); + for(; itSc!=m_scenes.end(); ++itSc) { + xmlNewChild(rootNode, NULL, BAD_CAST "Scene", NULL); + } + + //save zone + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + itZo->second->save(rootNode); + } + + //save midi + xmlNodePtr setNode = xmlNewChild(rootNode, NULL, + BAD_CAST "Midi", NULL); + xmlNewProp(setNode, BAD_CAST "device", + BAD_CAST m_currentMidiDeviceName.c_str()); + ostringstream oss1, oss2, oss3; + oss1<<m_midiChannel; + xmlNewProp(setNode, BAD_CAST "set_channel", + BAD_CAST oss1.str().c_str()); + oss2<<m_midiType; + xmlNewProp(setNode, BAD_CAST "set_type", + BAD_CAST oss2.str().c_str()); + oss3<<m_midiControl; + xmlNewProp(setNode, BAD_CAST "set_control", + BAD_CAST oss3.str().c_str()); + + + xmlDocSetRootElement(doc, rootNode); + xmlSaveFormatFileEnc(fileName.c_str(), doc, "UTF-8", 1); + xmlFreeDoc(doc); + xmlCleanupParser(); + m_fileName=fileName; + updateTitle(); + m_mainPanel->hide(); +} + +void Controlar::addZone(ZoneWidget* zone) { + unsigned int newID=0; + while(m_zones.find(newID)!=m_zones.end()) { + newID++; + } + zone->setID(newID); + m_zones[newID] = zone; + zone->callback(statZone, this); + add(zone); + m_zoneMenu->hide(); +} + +void Controlar::addGroup(GroupWidget* group) { + unsigned int newID=0; + while(m_groups.find(newID)!=m_groups.end()) { + newID++; + } + group->setID(newID); + m_groups[newID] = group; + add(group); +} + +void Controlar::moveAllZonesInsideGroupBy(GroupWidget* group, int dx, int dy) { + map<unsigned int, ZoneWidget*>::iterator itZo = m_zones.begin(); + for(; itZo!=m_zones.end(); ++itZo) { + if(itZo->second->x()>group->x() + && itZo->second->x()<group->x()+group->w() + && itZo->second->y()>group->y() + && itZo->second->y()<group->y()+group->h()) { + itZo->second->position(itZo->second->x()+dx, + itZo->second->y()+dy); + } + } +} + +void Controlar::cbZoneSelect(CutWindow* win) { + m_clickedZone->setCutWin(win); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneClear() { + m_clickedZone->setCutWin(NULL); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneDelete() { + m_zones.erase(m_clickedZone->getID()); + Fl::delete_widget(m_clickedZone); + m_zoneMenu->hide(); + m_zonePanel->hide(); +} + +void Controlar::cbZoneMove() { + +} + +void Controlar::cbZoneAlpha(const int& alpha) { + m_clickedZone->setAlpha(alpha); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneTrans(const ZoneWidget::ZONE_TRANSFO& trans) { + m_clickedZone->setTransformation(trans); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneShape(const ZoneWidget::ZONE_SHAPE& shape) { + m_clickedZone->setShape(shape); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneMidiLearn() { + m_clickedZone->learnMidi(); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneMidiEffect(const ZoneWidget::ZONE_INPUT_EFFECT& effect) { + m_clickedZone->setInputEffect(effect); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneCol(const ZoneWidget::ZONE_COLOR& col) { + m_clickedZone->setColor(col); + m_zoneMenu->hide(); +} + +void Controlar::cbZoneVisible(const ZoneWidget::ZONE_VISIBLE& vis) { + if(vis==ZoneWidget::VISIBLE_CURRENT) { + m_clickedZone->setVisible(m_currentScene); + } + else if(vis==ZoneWidget::VISIBLE_ALL) { + m_clickedZone->setVisible(); + } + m_zoneMenu->hide(); +} + +void Controlar::cbZoneUpdate(const ZoneWidget::ZONE_UPDATE& up) { + if(up==ZoneWidget::UPDATE_CURRENT) { + m_clickedZone->setUpdate(m_currentScene); + } + else if(up==ZoneWidget::UPDATE_ALL) { + m_clickedZone->setUpdate(); + } + m_zoneMenu->hide(); +} + +void Controlar::cbZoneContent(const ZoneWidget::ZONE_CONTENT& cont) { + m_clickedZone->setContent(cont); + m_zoneMenu->hide(); +} + +void Controlar::init() { + this->show(); +} + +Controlar::~Controlar() {} + +Controlar* Controlar::getInstance() { + static Controlar instance; + return &instance; +} + +void Controlar::cbQuit() { + remove(m_zonePanel); + remove(m_mainPanel); + exit(0); +} + +void Controlar::cbMidiDev(const int& dev) { + if(m_midiStream!=NULL) { + Pm_Close(m_midiStream); + } + Pm_OpenInput(&m_midiStream, dev, NULL, 100, NULL, NULL); + const PmDeviceInfo* info = Pm_GetDeviceInfo(dev); + m_currentMidiDeviceName = string(info->name); + m_mainMenu->hide(); + m_zoneMenu->hide(); +} + +void Controlar::setMidiDeviceFromName(const string& name) { + for(unsigned int d=0; d<m_midiDeviceNames.size(); ++d) { + if(m_midiDeviceNames[d].compare(name)==0) { + cbMidiDev(m_midiDevices[d]); + } + } +} + +void DetectionWindow::draw() { + #ifdef OSX + CGRect rect = CGRectMake(0,0,100,100); + CGRect deviceRect = CGContextConvertRectToDeviceSpace(fl_gc, rect); + Controlar::getInstance()->setDisplayScale(deviceRect.size.width + / rect.size.width); + #endif + Fl::delete_widget(this); +} + + +int main(int argc, char* argv[]) { + #ifndef GL + Fl::visual(FL_DOUBLE|FL_RGB); + #endif + + //first grab the display scale + DetectionWindow* detWin = new DetectionWindow(); + detWin->show(); + + //then init controllar + Controlar::getInstance()->init(); + fl_cursor(FL_CURSOR_NONE); + return Fl::run(); +} + + diff --git a/src/Controlar.hpp b/src/Controlar.hpp new file mode 100755 index 0000000000000000000000000000000000000000..13ed121b044184d7f3af4bbff0dbde3bd59f8172 --- /dev/null +++ b/src/Controlar.hpp @@ -0,0 +1,372 @@ +/*************************************************************************** + * Controlar.hpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef Controlar_h +#define Controlar_h + +#include <string> +#include <map> +#include <vector> +#include <sys/time.h> + +#ifdef GL +#include <FL/Fl_Gl_Window.H> +#else +#include <FL/Fl_Double_Window.H> +#endif + +#include <FL/Fl_Tile.H> +#include <FL/Fl_Pack.H> +#include <FL/Fl_Scroll.H> +#include <FL/Fl_Hold_Browser.H> +#include <FL/Fl_Menu_Button.H> +#include <FL/Fl_Toggle_Button.H> +#include <FL/Fl_Native_File_Chooser.H> +#include <FL/Fl.H> +#include <FL/fl_draw.H> +#include <FL/Fl_Overlay_Window.H> + +#include <portmidi.h> + +#include "osc/ip/IpEndpointName.h" +#include "osc/ip/UdpSocket.h" +#include "osc/osc/OscPacketListener.h" + +#include "FlipMenu.hpp" +#include "ZoneWidget.hpp" +#include "GroupWidget.hpp" +#include "CutWindowsManager.hpp" +class MainPanel; +class ZonePanel; + +#define IP_MTU_SIZE 2048 + +#ifdef GL +class Controlar: public Fl_Gl_Window, public osc::OscPacketListener { +#else +class Controlar: public Fl_Double_Window, public osc::OscPacketListener { +#endif + public: + static Controlar* getInstance(); + ~Controlar(); + void init(); + void load(const std::string& fileName); + void save(const std::string& fileName); + + void update(); + int handle(int event); + void draw(); + void resize(int x, int y, int w, int h); + CutWindow* getWindow(const std::string& name); + inline int getNbWindows() {return m_winsMan->getNbWindows();} + CutWindow* getWindow(const int&); + void addZone(ZoneWidget*); + void addGroup(GroupWidget*); + void moveAllZonesInsideGroupBy(GroupWidget*, int dx, int dy); + void refreshWindowList(); + + //ZONE CALLBACKS + static void statZone(Fl_Widget* w, void* f){ + Controlar *tmpf = static_cast<Controlar *>(f); + tmpf->cbZone(w); + } + void cbZone(Fl_Widget*); + + static void statZoneSelect(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneSelect(static_cast<CutWindow*>(f)); + } + void cbZoneSelect(CutWindow*); + static void statZoneClear(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneClear(); + } + void cbZoneClear(); + static void statZoneDelete(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneDelete(); + } + void cbZoneDelete(); + static void statZoneMove(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneMove(); + } + void cbZoneMove(); + static void statZoneAlpha(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneAlpha(*((int*)f)); + } + void cbZoneAlpha(const int& alpha); + static void statZoneShape(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneShape(*((ZoneWidget::ZONE_SHAPE*)f)); + } + void cbZoneShape(const ZoneWidget::ZONE_SHAPE& shape); + static void statZoneTrans(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneTrans(*((ZoneWidget::ZONE_TRANSFO*)f)); + } + void cbZoneTrans(const ZoneWidget::ZONE_TRANSFO& trans); + static void statZoneMidiLearn(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneMidiLearn(); + } + void cbZoneMidiLearn(); + static void statZoneMidiEffect(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneMidiEffect(*((ZoneWidget::ZONE_INPUT_EFFECT*)f)); + } + void cbZoneMidiEffect(const ZoneWidget::ZONE_INPUT_EFFECT& trans); + static void statZoneCol(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneCol(*((ZoneWidget::ZONE_COLOR*)f)); + } + void cbZoneCol(const ZoneWidget::ZONE_COLOR& col); + static void statZoneVisible(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneVisible(*((ZoneWidget::ZONE_VISIBLE*)f)); + } + void cbZoneVisible(const ZoneWidget::ZONE_VISIBLE& vis); + static void statZoneUpdate(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneUpdate(*((ZoneWidget::ZONE_UPDATE*)f)); + } + void cbZoneUpdate(const ZoneWidget::ZONE_UPDATE& up); + static void statZoneContent(Fl_Widget* w, void* f){ + Controlar::getInstance() + ->cbZoneContent(*((ZoneWidget::ZONE_CONTENT*)f)); + } + void cbZoneContent(const ZoneWidget::ZONE_CONTENT& up); + + //MAIN CALLBACKS + static void statOpen(Fl_Widget* w,void* f){ + Controlar *tmpf = static_cast<Controlar *>(f); + tmpf->cbOpen(); + } + void cbOpen(); + + static void statSave(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbSave(); + } + void cbSave(); + static void statSaveAs(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbSaveAs(); + } + void cbSaveAs(); + + static void statSceneNext(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbSceneNext(); + } + void cbSceneNext(); + static void statScenePrev(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbScenePrev(); + } + void cbScenePrev(); +/* + static void statSetDup(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbSetDuplicate(); + } + void cbSetDuplicate(); +*/ + static void statSceneDel(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbSceneDelete(); + } + void cbSceneDelete(); + static void statSceneLearn(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbSceneLearn(); + } + void cbSceneLearn(); + void setScene(const int&); + + static void statFlip(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbFlip(); + } + void cbFlip(); + inline bool isFlipped(){return m_flipped;} + + static void statFullscreen(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbFullscreen(); + } + void cbFullscreen(); + static void statMidiDev(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbMidiDev(*(int*)(f)); + } + void cbMidiDev(const int&); + void setMidiDeviceFromName(const std::string&); + + static void statQuit(Fl_Widget* w,void* f){ + Controlar *tmpf = static_cast<Controlar *>(f); + tmpf->cbQuit(); + } + void cbQuit(); + static void statZoneMenuWin(Fl_Widget* w, void* f){ + static_cast<ZoneWidget*>(w)->setCutWin(static_cast<CutWindow*>(f)); + } + + void parseMidi(); + void openMidiDevice(const std::string& devName); + void closeMidiDevice(const std::string& devName); + void refreshMidiStreams(); + void detectMidiDevices(); + void updateTitle(); + + ZoneWidget* startDuplicating(ZoneWidget* dupWid); + + virtual void ProcessMessage(const osc::ReceivedMessage& m, + const IpEndpointName& remoteEndpoint); + + inline const int& getDisplayScale(){return m_displayScale;} + inline void setDisplayScale(const int& s){m_displayScale=s;} + +#ifdef GL + void initGL(); + inline GLint getZoneSizeUniform(){return m_zoneSizeUniform;} + inline GLint getZonePosUniform(){return m_zonePosUniform;} + inline GLint getCutSizeUniform(){return m_cutSizeUniform;} + inline GLint getCutPosUniform(){return m_cutPosUniform;} + inline GLint getZoneStateUniform(){return m_zoneStateUniform;} + inline GLint getZoneWindowUniform(){return m_zoneWindowUniform;} + inline GLint getZoneCoordsUniform(){return m_zoneCoordsUniform;} + inline GLint getZoneFilledUniform(){return m_zoneFilledUniform;} + inline GLint getZoneAlphaUniform(){return m_zoneAlphaUniform;} + inline GLint getZoneInvertUniform(){return m_zoneInvertUniform;} + inline GLint getGroupSizeUniform(){return m_groupSizeUniform;} + inline GLint getGroupPosUniform(){return m_groupPosUniform;} + inline GLint getGroupEditingUniform(){return m_groupEditingUniform;} + inline GLint getGroupHighlightUniform(){return m_groupHighlightUniform;} + inline GLint getMenuSizeUniform(){return m_menuSizeUniform;} + inline GLint getMenuPosUniform(){return m_menuPosUniform;} + inline GLuint getFirstTextureID(){return m_firstTextureID;} + inline GLuint getSecondTextureID(){return m_secondTextureID;} + inline const std::string& getCurrentWindowName() { + return m_currentWindowName; + } + inline void setCurrentWindowName(const std::string& n) { + m_currentWindowName=n; + } +#endif + + protected: + friend void* oscThreadFunction(void*); + + protected: + UdpListeningReceiveSocket* m_socket; + + private: + Controlar(); + std::string m_title; + std::string m_fileName; + int m_drawStartX, m_drawStartY; + int m_drawCurX, m_drawCurY; + bool m_drawing, m_drawingGroup; + FlipMenu* m_zoneMenu; + FlipMenu* m_mainMenu; + MainPanel* m_mainPanel; + ZonePanel* m_zonePanel; + CutWindowsManager* m_winsMan; + //std::vector<std::map<unsigned int, ZoneWidget*> > m_zonesSets; + std::map<int, std::string> m_scenes; + std::map<unsigned int, ZoneWidget*> m_zones; + std::map<unsigned int, GroupWidget*> m_groups; + int m_alphaLevels[4]; + int m_menuValues[10]; + //unsigned int m_currentSet; + int m_currentScene; + ZoneWidget* m_clickedZone; + bool m_flipped; + bool m_fullscreen; + bool m_duplicating; + ZoneWidget* m_duplicatedZone; + PortMidiStream* m_midiStream; + std::map<std::string, int> m_midiDevMap; + std::map<std::string, PortMidiStream*> m_midiStreamsMap; + std::vector<PortMidiStream*> m_midiStreamsVec; + bool m_midiLearning; + int m_midiType; + int m_midiChannel; + int m_midiControl; + std::vector<int> m_midiDevices; + std::vector<std::string> m_midiDeviceNames; + std::string m_currentMidiDeviceName; + pthread_t m_oscThread; + pthread_mutex_t m_zonesMutex; + int m_askedScene; + int m_displayScale; + +#ifdef GL + GLuint m_vertexArrayID; + GLuint m_vertexBuffer; + GLfloat m_vertexBufferData[18]; + + GLuint m_zoneProgram; + GLint m_zoneWinSizeUniform; + GLint m_zoneSizeUniform; + GLint m_zonePosUniform; + GLint m_cutSizeUniform; + GLint m_cutPosUniform; + GLint m_zoneMirrorUniform; + GLint m_zoneStateUniform; + GLint m_zoneFilledUniform; + GLint m_zoneAlphaUniform; + GLint m_zoneInvertUniform; + GLint m_zoneWindowUniform; + GLint m_zoneCoordsUniform; + GLint m_zoneOuterUniform; + GLint m_zoneInnerUniform; + + GLuint m_cursorProgram; + GLint m_cursorWinSizeUniform; + GLint m_cursorPosUniform; + GLint m_cursorMirrorUniform; + + GLuint m_drawingProgram; + GLint m_drawWinSizeUniform; + GLint m_drawPosUniform; + GLint m_drawSizeUniform; + GLint m_drawMirrorUniform; + GLint m_drawGroupUniform; + + GLuint m_menuProgram; + GLint m_menuWinSizeUniform; + GLint m_menuPosUniform; + GLint m_menuSizeUniform; + GLint m_menuMirrorUniform; + GLint m_menuTextureUniform; + GLuint m_firstTextureID; + GLuint m_secondTextureID; + std::string m_currentWindowName; + + GLuint m_groupProgram; + GLint m_groupWinSizeUniform; + GLint m_groupPosUniform; + GLint m_groupSizeUniform; + GLint m_groupMirrorUniform; + GLint m_groupEditingUniform; + GLint m_groupHighlightUniform; +#else + int m_cursPosX; + int m_cursPosY; +#endif +}; + +class DetectionWindow: public Fl_Double_Window { + public: + DetectionWindow():Fl_Double_Window(10, 10, ""){} + void draw(); +}; + +#endif + diff --git a/src/CutWindow.hpp b/src/CutWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3fdffd3f79a53872f7bacdb59e25c981cb2d9e01 --- /dev/null +++ b/src/CutWindow.hpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * CutWindow.hpp + * Part of Controlar + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef CutWindow_h +#define CutWindow_h + +#include <vector> +#include <map> +#include <string> + +#include <FL/Fl.H> +#include <FL/Fl_RGB_Image.H> + +class CutWindowsManager; +#include "ZoneWidget.hpp" + +class CutWindow { + public : + CutWindow(CutWindowsManager* man):m_winsMan(man){} + virtual ~CutWindow(){} + + void setName(const std::string& name){m_name=name;} + const std::string& getName(){return m_name;} + virtual void getPixels(const int& x, const int& y, + const int& sx, const int& sy, + const std::vector<int>& srcIndices, + const std::vector<std::vector<int> >& srcCoords, + const std::vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg)=0; + + virtual int computeNbPixPerRow(const int& srcW, const int& srcH)=0; + inline const int& getWidth(){return m_width;} + inline const int& getHeight(){return m_height;} + inline const int& getOffsetX(){return m_offsetX;} + inline const int& getOffsetY(){return m_offsetY;} + inline const int& getPixPerRow(){return m_pixPerRow;} + inline bool isBGR(){return m_isBGR;} + inline bool needsOffset(){return m_needsOffset;} + inline void setWinID(const int& id){m_winID=id;} + inline const int& getWinID(){return m_winID;} + + virtual uchar* grabImage()=0; + virtual void releaseImage()=0; + + virtual void storeImage(const int& id)=0; + virtual uchar* retrieveImage(const int& id)=0; + + protected: + int m_winID; + std::string m_name; + int m_posX, m_posY, m_width, m_height; + int m_offsetX, m_offsetY, m_pixPerRow; + bool m_isBGR; + bool m_needsOffset; + CutWindowsManager* m_winsMan; + bool m_grabbed; + uchar* m_imgData; + uchar* m_defaultImgData; + + std::map<int, uchar*> m_storedImgData; +}; + +#endif + diff --git a/src/CutWindowsManager.hpp b/src/CutWindowsManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6d44f66012b04d73882b26ccdb6ce4a46bfd4e76 --- /dev/null +++ b/src/CutWindowsManager.hpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * CutWindowsManager.hpp + * Part of Over + * 2013 Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef CutWindowsManager_h +#define CutWindowsManager_h + +#include <vector> +#include <string> +#include <iostream> + +#include "CutWindow.hpp" + +class CutWindowsManager { + public : + CutWindowsManager(){} + virtual ~CutWindowsManager(){} + + virtual void updateWindowsList()=0; + std::vector<CutWindow*>& editWindowList(){return m_windowList;} + CutWindow* getWindow(const std::string& name) { + CutWindow* res=NULL; + std::vector<CutWindow*>::iterator itWin=m_windowList.begin(); + for(; itWin!=m_windowList.end(); ++itWin) { + if((*itWin)->getName().compare(name)==0 && name.compare("")!=0){ + res=(*itWin); + } + } + return res; + } + int getNbWindows(){return m_windowList.size();} + + protected: + std::vector<CutWindow*> m_windowList; + std::map<unsigned int, CutWindow*> m_windowMap; +}; + +#endif + diff --git a/src/FlipGroup.cpp b/src/FlipGroup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a06987b76d3b13a78938bc0b84de30b2a831393 --- /dev/null +++ b/src/FlipGroup.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** + * FlipGroup.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "FlipGroup.hpp" +#include <FL/Fl_Button.H> +#include <FL/fl_draw.H> +#include <algorithm> +#include <iostream> +#include "Controlar.hpp" + +using namespace std; + +FlipGroup::FlipGroup(int x, int y, int w, int h): Fl_Group(x, y, w, h, "") { + end(); + box(FL_FLAT_BOX); + m_wasChanged=true; +} + +void FlipGroup::draw() { + Controlar* cont = Controlar::getInstance(); + if(m_wasChanged) { + Fl_Image_Surface* surf = new Fl_Image_Surface(w(), h()); + surf->set_current(); + fl_draw_box(box(), 0, 0, + w(), h(), color()); + for(int c=0; c<children(); ++c) { + surf->draw(child(c), + child(c)->x()-x(), + child(c)->y()-y()); + } + m_flipImg = surf->image(); + delete surf; + Fl_Display_Device::display_device()->set_current(); + m_wasChanged=false; + } +#ifdef GL + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cont->getFirstTextureID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + w(), h(), + 0, GL_RGB, GL_UNSIGNED_BYTE, + m_flipImg->array); + glUniform2f(cont->getMenuPosUniform(), x(), y()); + glUniform2f(cont->getMenuSizeUniform(), w(), h()); + glDrawArrays(GL_TRIANGLES, 0, 6); +#else + fl_draw_image(statDrawFlipImg, this, + x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), m_flipImg->d()); +#endif +} + +int FlipGroup::handle(int event) { + int res=Fl_Group::handle(event); + if(res) { + refresh(); + } + return 1; +} + +void FlipGroup::resize(int x, int y, int w, int h) { + Fl_Group::resize(x,y,w,h); + m_wasChanged=true; +} + +void FlipGroup::refresh() { + m_wasChanged=true; + redraw(); +} + +void FlipGroup::addWid(Fl_Widget* wid) { + m_wasChanged=true; + add(wid); + wid->box(FL_BORDER_BOX); + wid->visible_focus(0); +} + diff --git a/src/FlipGroup.hpp b/src/FlipGroup.hpp new file mode 100644 index 0000000000000000000000000000000000000000..312e34c66d843df09806b16ac8ababaf5dcaaaa6 --- /dev/null +++ b/src/FlipGroup.hpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * FlipGroup.hpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef FlipGroup_h +#define FlipGroup_h + +#include <string> +#include <map> +#include <vector> + +#include <FL/Fl_Pack.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Image_Surface.H> + +class FlipGroup: public Fl_Group { + public : + FlipGroup(int x, int y, int w, int h); + void draw(); + int handle(int event); + void addWid(Fl_Widget*); + void resize(int x, int y, int w, int h); + void refresh(); + + protected: + Fl_RGB_Image* m_flipImg; + bool m_wasChanged; +}; + +#endif diff --git a/src/FlipMenu.cpp b/src/FlipMenu.cpp new file mode 100644 index 0000000000000000000000000000000000000000..422dd92ed99e9e6e1a687cf9e171b38f6697f98a --- /dev/null +++ b/src/FlipMenu.cpp @@ -0,0 +1,377 @@ +/*************************************************************************** + * FlipMenu.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "FlipMenu.hpp" +#include <FL/Fl_Button.H> +#include <FL/fl_draw.H> +#include <algorithm> +#include <iostream> +#include "Controlar.hpp" + +using namespace std; + +FlipMenu::FlipMenu():Fl_Pack(0, 0, 100, 100, "") { + type(Fl_Pack::VERTICAL); + end(); +} + +void FlipMenu::addWithSub(const std::string& itemName,Fl_Callback* cb,void* p) { + //parse submenu, create flip menu if needed + size_t pos = itemName.find_first_of('/'); + if(pos==string::npos) { + add(itemName, cb, p); + } + else { + string subName = itemName.substr(0, pos); + addSub(subName); + string subItemName = itemName.substr(pos+1); + m_subMenuMap[subName]->add(subItemName, cb, p); + } +} + +void FlipMenu::add(const std::string& itemName, Fl_Callback* cb, void* p) { + FlipItem* but = new FlipItem(this); + but->copy_label(itemName.c_str()); + but->callback(cb, p); + Fl_Pack::add(but); + resize(0, 0, + max(w(), + int(float(itemName.length()*FL_NORMAL_SIZE)/4.0)*4), + children()*20); +} + +void FlipMenu::addSub(const string& subName) { + if(m_subMenuMap.find(subName)==m_subMenuMap.end()) { + m_subMenuMap[subName] = new FlipMenu(); + FlipItemMenu* men = new FlipItemMenu(this, m_subMenuMap[subName]); + men->copy_label(subName.c_str()); + Fl_Pack::add(men); + } +} + +FlipMenu* FlipMenu::getSub(const string& subName) { + FlipMenu* res=NULL; + if(m_subMenuMap.find(subName)!=m_subMenuMap.end()) { + res=m_subMenuMap[subName]; + } + return res; +} + +void FlipMenu::clear() { + map<string, FlipMenu*>::iterator itSub = m_subMenuMap.begin(); + for(; itSub!=m_subMenuMap.end(); ++itSub) { + itSub->second->clear(); + Controlar::getInstance()->remove(itSub->second); + Fl::delete_widget(itSub->second); + } + m_subMenuMap.clear(); + Fl_Pack::clear(); + resize(x(), y(), 100, 100); +} + +void FlipMenu::hide() { + Fl_Pack::hide(); + hideSubMenus(); +} + +void FlipMenu::draw() { + Fl_Pack::draw(); +} + +void FlipMenu::hideSubMenus() { + map<string, FlipMenu*>::iterator itSub = m_subMenuMap.begin(); + for(; itSub!=m_subMenuMap.end(); ++itSub) { + itSub->second->hide(); + } +} + + +//--------------------------------ITEM MENU +FlipItemMenu::FlipItemMenu(FlipMenu* men, FlipMenu* subMen): + Fl_Button(0,0,20,20,""), + m_menu(men), + m_subMenu(subMen) { + align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); + box(FL_THIN_DOWN_BOX); + visible_focus(false); + m_wasResized=true; + m_highlight=false; +} + +int FlipItemMenu::handle(int event) { + switch(event) { + case FL_ENTER: { + m_menu->hideSubMenus(); + color(FL_SELECTION_COLOR); + m_subMenu->position(x()+w(), y()); + Controlar* cont = Controlar::getInstance(); + cont->insert(*m_subMenu, cont->children()); + m_subMenu->show(); + m_highlight=true; + return 1; + }break; + case FL_LEAVE: { + color(FL_BACKGROUND_COLOR); + m_highlight=false; + return 1; + }break; + default:break; + } + return 0; +} + +void FlipItemMenu::resize(int x, int y, int w, int h) { + Fl_Button::resize(x,y,w,h); + m_wasResized=true; +} + +void FlipItemMenu::draw() { + Controlar* cont = Controlar::getInstance(); + if(m_wasResized) { + Fl_Image_Surface* surf = new Fl_Image_Surface(w(), h()); + surf->set_current(); + fl_draw_box(box(), 0, 0, + w(), h(), color()); + fl_color(FL_BLACK); + fl_draw(label(), + 0, 0, + w(), h(), + align(), NULL, 0); + m_flipImg = surf->image(); + delete surf; + + Fl_Display_Device::display_device()->set_current(); + surf = new Fl_Image_Surface(w(), h()); + surf->set_current(); + fl_draw_box(box(), 0, 0, + w(), h(), FL_SELECTION_COLOR); + fl_color(FL_BLACK); + fl_draw(label(), + 0, 0, + w(), h(), + align(), NULL, 0); + m_flipImgH = surf->image(); + delete surf; + + Fl_Display_Device::display_device()->set_current(); + m_wasResized=false; + } + +#ifdef GL + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cont->getFirstTextureID()); + if(m_highlight) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + w(), h(), + 0, GL_RGB, GL_UNSIGNED_BYTE, + m_flipImgH->array); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + w(), h(), + 0, GL_RGB, GL_UNSIGNED_BYTE, + m_flipImg->array); + } + glUniform2f(cont->getMenuPosUniform(), x(), y()); + glUniform2f(cont->getMenuSizeUniform(), w(), h()); + glDrawArrays(GL_TRIANGLES, 0, 6); +#else + if(m_highlight) { + fl_draw_image(statDrawFlipImg, this, + x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), m_flipImgH->d()); + } + else { + fl_draw_image(statDrawFlipImg, this, + x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), m_flipImg->d()); + } +#endif + if(m_subMenu->visible()) { + m_subMenu->draw(); + } +} + +void FlipItemMenu::drawFlipImg(int x, int y, int w, uchar* buf) { + if(Controlar::getInstance()->isFlipped()) { + if(m_highlight) { + memcpy(buf, + &m_flipImgH->array[((m_flipImgH->h()-y)*m_flipImgH->w()+x) + *m_flipImgH->d()], + w*m_flipImgH->d()); + } + else { + memcpy(buf, + &m_flipImg->array[((m_flipImg->h()-y)*m_flipImg->w()+x) + *m_flipImg->d()], + w*m_flipImg->d()); + } + } + else { + if(m_highlight) { + memcpy(buf, + &m_flipImgH->array[(y*m_flipImgH->w()+x)*m_flipImgH->d()], + w*m_flipImgH->d()); + } + else { + memcpy(buf, + &m_flipImg->array[(y*m_flipImg->w()+x)*m_flipImg->d()], + w*m_flipImg->d()); + } + } +} + +//-------------------------------ITEM +FlipItem::FlipItem(FlipMenu* men):Fl_Button(0,0,20,20,""), + m_menu(men) { + align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE); + box(FL_THIN_UP_BOX); + visible_focus(false); + m_wasResized=true; + m_highlight=false; +} + +int FlipItem::handle(int event) { + switch(event) { + case FL_ENTER: { + m_menu->hideSubMenus(); + color(FL_SELECTION_COLOR); + m_highlight=true; + return 1; + }break; + case FL_LEAVE: { + color(FL_BACKGROUND_COLOR); + m_highlight=false; + return 1; + }break; + case FL_PUSH: { + if(Fl::event_button()==FL_LEFT_MOUSE) { + do_callback(); + color(FL_BACKGROUND_COLOR); + m_menu->hide(); + return 1; + } + }break; + default:break; + } + return 0; +} + +void FlipItem::resize(int x, int y, int w, int h) { + Fl_Button::resize(x,y,w,h); + m_wasResized=true; +} + +void FlipItem::draw() { + + Controlar* cont = Controlar::getInstance(); + if(m_wasResized) { + Fl_Image_Surface* surf = new Fl_Image_Surface(w(), h()); + surf->set_current(); + fl_draw_box(box(), 0, 0, + w(), h(), color()); + fl_color(FL_BLACK); + fl_draw(label(), + 0, 0, + w(), h(), + align(), NULL, 0); + m_flipImg = surf->image(); + delete surf; + + Fl_Display_Device::display_device()->set_current(); + surf = new Fl_Image_Surface(w(), h()); + surf->set_current(); + fl_draw_box(box(), 0, 0, + w(), h(), FL_SELECTION_COLOR); + fl_color(FL_BLACK); + fl_draw(label(), + 0, 0, + w(), h(), + align(), NULL, 0); + m_flipImgH = surf->image(); + + delete surf; + Fl_Display_Device::display_device()->set_current(); + m_wasResized=false; + } + +#ifdef GL + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cont->getFirstTextureID()); + if(m_highlight) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + w(), h(), + 0, GL_RGB, GL_UNSIGNED_BYTE, + m_flipImgH->array); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, + w(), h(), + 0, GL_RGB, GL_UNSIGNED_BYTE, + m_flipImg->array); + } + glUniform2f(cont->getMenuPosUniform(), x(), y()); + glUniform2f(cont->getMenuSizeUniform(), w(), h()); + glDrawArrays(GL_TRIANGLES, 0, 6); +#else + if(m_highlight) { + fl_draw_image(statDrawFlipImg, this, + x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), m_flipImgH->d()); + } + else { + fl_draw_image(statDrawFlipImg, this, + x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), m_flipImg->d()); + } +#endif +} + +void FlipItem::drawFlipImg(int x, int y, int w, uchar* buf) { + if(Controlar::getInstance()->isFlipped()) { + if(m_highlight) { + memcpy(buf, + &m_flipImgH->array[((m_flipImgH->h()-y)*m_flipImgH->w()+x) + *m_flipImgH->d()], + w*m_flipImgH->d()); + } + else { + memcpy(buf, + &m_flipImg->array[((m_flipImg->h()-y)*m_flipImg->w()+x) + *m_flipImg->d()], + w*m_flipImg->d()); + } + } + else { + if(m_highlight) { + memcpy(buf, + &m_flipImgH->array[(y*m_flipImgH->w()+x)*m_flipImgH->d()], + w*m_flipImgH->d()); + } + else { + memcpy(buf, + &m_flipImg->array[(y*m_flipImg->w()+x)*m_flipImg->d()], + w*m_flipImg->d()); + } + } +} + diff --git a/src/FlipMenu.hpp b/src/FlipMenu.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bcbaa875e137168d72f90fc12f14416728baf0b1 --- /dev/null +++ b/src/FlipMenu.hpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * FlipMenu.hpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef FlipMenu_h +#define FlipMenu_h + +#include <string> +#include <map> +#include <vector> + +#include <FL/Fl_Pack.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Image_Surface.H> +#include <map> +#include <string> + +class FlipItemMenu; +class FlipItem; + +class FlipMenu: public Fl_Pack { + public : + FlipMenu(); + void draw(); + void addWithSub(const std::string& itemName, Fl_Callback*, void* p); + void add(const std::string& itemName, Fl_Callback*, void* p); + void addSub(const std::string& subName); + FlipMenu* getSub(const std::string& subName); + void hide(); + void clear(); + void hideSubMenus(); + + private: + std::map<std::string, FlipMenu*> m_subMenuMap; + +}; + +class FlipItemMenu : public Fl_Button { + public : + FlipItemMenu(FlipMenu* menu, FlipMenu* subMenu); + int handle(int event); + void draw(); + void resize(int x, int y, int w, int h); + static void statDrawFlipImg(void *data,int x,int y, int w, uchar *buf) { + FlipItemMenu *item = static_cast<FlipItemMenu*>(data); + item->drawFlipImg(x, y, w, buf); + } + void drawFlipImg(int x, int y, int w, uchar* buf); + + private: + FlipMenu* m_menu; + FlipMenu* m_subMenu; + Fl_RGB_Image* m_flipImg; + Fl_RGB_Image* m_flipImgH; + bool m_wasResized; + bool m_highlight; +}; + +class FlipItem : public Fl_Button { + public : + FlipItem(FlipMenu*); + int handle(int event); + void draw(); + void resize(int x, int y, int w, int h); + static void statDrawFlipImg(void *data,int x,int y, int w, uchar *buf) { + FlipItem *item = static_cast<FlipItem*>(data); + item->drawFlipImg(x, y, w, buf); + } + void drawFlipImg(int x, int y, int w, uchar* buf); + + private: + FlipMenu* m_menu; + Fl_RGB_Image* m_flipImg; + Fl_RGB_Image* m_flipImgH; + bool m_wasResized; + bool m_highlight; +}; + +#endif + diff --git a/src/GroupWidget.cpp b/src/GroupWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c439fffa7fa78dad16b8c32ec2e8d889336c0a7 --- /dev/null +++ b/src/GroupWidget.cpp @@ -0,0 +1,203 @@ +/*************************************************************************** + * GroupWidget.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "GroupWidget.hpp" +#include <iostream> +#include <sstream> +#include <cmath> + +#include "Controlar.hpp" + +using namespace std; + +GroupWidget::GroupWidget(int x, int y, int w, int h):Fl_Widget(x,y,w,h,"") { + box(FL_FLAT_BOX); + m_dragging=false; + m_highlight=false; + m_editing=false; + m_scaleX=1; + m_scaleY=1; +} + +void GroupWidget::copy(GroupWidget* original) { + resize(original->x(), original->y(), original->w(), original->h()); +} + +GroupWidget::~GroupWidget() {} + +void GroupWidget::draw() { + Controlar* cont = Controlar::getInstance(); + + //draw +#ifdef GL + glUniform1f(cont->getGroupHighlightUniform(), m_highlight); + glUniform1f(cont->getGroupEditingUniform(), m_editing); + glUniform2f(cont->getGroupSizeUniform(), w(), h()); + glUniform2f(cont->getGroupPosUniform(), x(), y()); + glDrawArrays(GL_TRIANGLES, 0, 6); +#else + //process drawing + fl_rect(x(), cont->isFlipped()?cont->h()-y()-h():y(),w(),h(),FL_DARK_GREEN); + if(m_highlight) { + fl_rect(x(), cont->isFlipped()?cont->h()-y()-h():y(),w(),h(),FL_GREEN); + } + +#endif +} + +int GroupWidget::handle(int event) { + int res=0; + switch(event) { + case FL_PUSH:{ + switch(Fl::event_button()) { + case FL_RIGHT_MOUSE: { + do_callback(); + res=1; + }break; + case FL_LEFT_MOUSE: { + if(Fl::event_shift()) { + startDraggingGroup(); + m_editing=true; + m_dragging=true; + res=1; + } + }break; + default:break; + } + }break; + case FL_DRAG: { + if(m_dragging) { + if(Fl::event_button()==FL_LEFT_MOUSE) { + if(Fl::event_command()) { + size(m_dragW+Fl::event_x(), m_dragH+Fl::event_y()); + } + else { + Controlar* cont = Controlar::getInstance(); + cont->moveAllZonesInsideGroupBy(this, + m_dragX+Fl::event_x()-x(), + m_dragY+Fl::event_y()-y()); + position(m_dragX+Fl::event_x(), + m_dragY+Fl::event_y()); + } + res=1; + } + } + }break; + case FL_SHORTCUT: { + if(m_highlight && Fl::event_key()==FL_Shift_L) { + m_editing=true; + res=1; + } + }break; + case FL_KEYUP: { + if(m_highlight && Fl::event_key()==FL_Shift_L) { + m_editing=false; + res=1; + } + }break; + case FL_RELEASE: { + if(m_dragging) { + m_editing=false; + m_dragging=false; + res=1; + } + }break; + case FL_MOVE:{ + m_highlight=true; + if(Fl::event_shift()) { + m_editing=true; + res=1; + } + }break; + case FL_ENTER: { + m_highlight=true; + res=1; + }break; + case FL_LEAVE: { + m_highlight=false; + res=1; + }break; + default:break; + } + if(res==1) { + parent()->redraw(); + } + return res; +} + +void GroupWidget::startDraggingGroup() { + m_dragX=x()-Fl::event_x(); + m_dragY=y()-Fl::event_y(); + m_dragW=w()-Fl::event_x(); + m_dragH=h()-Fl::event_y(); +} + +void GroupWidget::resize(int nx, int ny, int nw, int nh) { + Fl_Widget::resize(nx, ny, nw, nh); +} + +void GroupWidget::load(xmlNodePtr groupNode) { + char* value = NULL; + + int x=0,y=0,w=20,h=20; + value = (char*)xmlGetProp(groupNode,(xmlChar*)"group_x"); + if(value!=NULL) { + x = atoi(value); + } + value = (char*)xmlGetProp(groupNode,(xmlChar*)"group_y"); + if(value!=NULL) { + y = atoi(value); + } + value = (char*)xmlGetProp(groupNode,(xmlChar*)"group_w"); + if(value!=NULL) { + w = atoi(value); + } + value = (char*)xmlGetProp(groupNode,(xmlChar*)"group_h"); + if(value!=NULL) { + h = atoi(value); + } + resize(x,y,w,h); +} + +void GroupWidget::save(xmlNodePtr parentNode) { + xmlNodePtr newNode = xmlNewChild(parentNode, NULL, + BAD_CAST "Group", NULL); + ostringstream oss1, oss2, oss3, oss4, oss5, + oss6, oss7, oss8, oss9, oss10, + oss11, oss12, oss13, oss14, oss15, + oss16, oss17; + oss5<<x(); + xmlNewProp(newNode, BAD_CAST "group_x", + BAD_CAST oss5.str().c_str()); + oss6<<y(); + xmlNewProp(newNode, BAD_CAST "group_y", + BAD_CAST oss6.str().c_str()); + oss7<<w(); + xmlNewProp(newNode, BAD_CAST "group_w", + BAD_CAST oss7.str().c_str()); + oss8<<h(); + xmlNewProp(newNode, BAD_CAST "group_h", + BAD_CAST oss8.str().c_str()); +} + + diff --git a/src/GroupWidget.hpp b/src/GroupWidget.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dffc234a9b958e40fb877c0ca7088c31c448fdc3 --- /dev/null +++ b/src/GroupWidget.hpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * GroupWidget.hpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GroupWidget_h +#define GroupWidget_h + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/fl_draw.H> +#include <GL/glew.h> +#include <FL/gl.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include <sys/time.h> +#include <string> +#include <vector> + +class GroupWidget : public Fl_Widget { + + public: + GroupWidget(int x=0, int y=0, int w=10, int h=10); + ~GroupWidget(); + void copy(GroupWidget*); + void draw(); + int handle(int event); + void save(xmlNodePtr parentNode); + void load(xmlNodePtr); + void resize(int x, int y, int w, int h); + inline void setID(const unsigned int& id){m_id=id;} + inline const unsigned int& getID(){return m_id;} + void startDraggingGroup(); + void forceDraggingGroup(); + + private: + unsigned int m_id; + bool m_dragging; + bool m_highlight; + bool m_editing; + float m_dragX, m_dragY; + float m_dragW, m_dragH; + float m_dragStartX, m_dragStartY; + float m_scaleX, m_scaleY; + +#ifdef GL + GLint m_sizeUniform; + GLint m_posUniform; + GLint m_editingUniform; +#endif +}; + + +#endif + diff --git a/src/MainPanel.cpp b/src/MainPanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..053775cdc46034d1c47e259fbcb0964d39151527 --- /dev/null +++ b/src/MainPanel.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + * MainPanel.cpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "MainPanel.hpp" +#include <FL/Fl_Button.H> +#include <FL/fl_draw.H> +#include <algorithm> +#include <iostream> +#include "Controlar.hpp" + +using namespace std; + +MainPanel::MainPanel(): FlipGroup(0, 0, 240, 160) { + int labW=60; + int butW=40; + int smButW=20; + int butH=20; + int sep=5; + Fl_Pack* fileP = new Fl_Pack(sep, sep, (butW+sep)*3, butH, "File"); + fileP->type(Fl_Pack::HORIZONTAL); + fileP->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + fileP->spacing(sep); + fileP->box(FL_NO_BOX); + add(fileP); + fileP->add(new Fl_Group(0,0,labW,0,"")); + Fl_Button* opBut = new Fl_Button(0, 0, butW, butH, "open"); + opBut->callback(statOpen,this); + fileP->add(opBut); + Fl_Button* saBut = new Fl_Button(0, 0, butW, butH, "save"); + saBut->callback(statSave,this); + fileP->add(saBut); + Fl_Button* quBut = new Fl_Button(0, 0, butW, butH, "quit"); + quBut->callback(statQuit,this); + fileP->add(quBut); + + Fl_Pack* disP = new Fl_Pack(sep, sep+(butH+sep), + (butW+sep)*3, butH, "Display"); + disP->type(Fl_Pack::HORIZONTAL); + disP->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); + disP->spacing(sep); + disP->box(FL_NO_BOX); + add(disP); + disP->add(new Fl_Group(0,0,labW,0,"")); + Fl_Button* fsBut = new Fl_Button(0, 0, butW*2, butH, "fullscreen"); + fsBut->callback(statFullscreen,this); + disP->add(fsBut); + Fl_Button* mirBut = new Fl_Button(0, 0, butW*2, butH, "mirror"); + mirBut->callback(statMirror,this); + disP->add(mirBut); + + int brW=160; + int brH=70; + Fl_Group* inps = new Fl_Group(sep,sep+(butH+sep)*2,brW+labW, brH, "Inputs"); + inps->box(FL_NO_BOX); + inps->align(FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE); + add(inps); + m_midiBr = new Fl_Multi_Browser(sep*2+labW, sep+(butH+sep)*2, brW, brH, ""); + m_midiBr->align(FL_ALIGN_TOP); + m_midiBr->has_scrollbar(Fl_Browser_::VERTICAL_ALWAYS); + m_midiBr->callback(statMidiBr, this); + inps->add(m_midiBr); + + Fl_Pack* scnP = new Fl_Pack(sep, sep+(butH+sep)*2+brH+sep, + brW, butH, "Scenes"); + scnP->align(FL_ALIGN_TOP|FL_ALIGN_LEFT|FL_ALIGN_INSIDE); + scnP->type(Fl_Pack::HORIZONTAL); + scnP->spacing(sep); + scnP->box(FL_NO_BOX); + add(scnP); + scnP->add(new Fl_Group(0, 0, labW, 0, "")); + Fl_Button* prevSc = new Fl_Button(0, 0, smButW, butH, "<"); + prevSc->callback(statScenePrev, this); + scnP->add(prevSc); + m_sceneOut = new Fl_Value_Output(0, 0, butW, butH, ""); + m_sceneOut->value(0); + scnP->add(m_sceneOut); + Fl_Button* nextSc = new Fl_Button(0, 0, smButW, butH, ">"); + nextSc->callback(statSceneNext, this); + scnP->add(nextSc); + Fl_Button* delSc = new Fl_Button(0, 0, smButW, butH, "x"); + delSc->callback(statSceneDel, this); + scnP->add(delSc); + Fl_Button* learnSc = new Fl_Button(0, 0, butW, butH, "learn"); + learnSc->callback(statSceneLearn, this); + scnP->add(learnSc); +} + +MainPanel* MainPanel::getInstance() { + static MainPanel instance; + return &instance; +} + + +void MainPanel::cbMidiBr() { + for(int i=1; i<=m_midiBr->size(); ++i) { + if(m_midiBr->selected(i)) { + Controlar::getInstance()->openMidiDevice(m_midiBr->text(i)); + } + else { + Controlar::getInstance()->closeMidiDevice(m_midiBr->text(i)); + } + } +} + +void MainPanel::setMidiDevices(const vector<string>& devs, + const vector<bool>& opened) { + m_midiBr->clear(); + vector<string>::const_iterator itDev = devs.begin(); + vector<bool>::const_iterator itOp = opened.begin(); + int el=1; + for(; itDev!=devs.end(); ++itDev, ++itOp, ++el) { + m_midiBr->add((*itDev).c_str(), NULL); + if((*itOp)) { + m_midiBr->select(el); + } + } +} + + diff --git a/src/MainPanel.hpp b/src/MainPanel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a2bd11e6f3c680ed280348380595cea8f1cf4316 --- /dev/null +++ b/src/MainPanel.hpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * MainPanel.hpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef MainPanel_h +#define MainPanel_h + +#include <FL/Fl_Multi_Browser.H> +#include <FL/Fl_Value_Output.H> + +#include "Controlar.hpp" +#include "FlipGroup.hpp" + +class MainPanel: public FlipGroup { + public : + static MainPanel* getInstance(); + ~MainPanel(){}; + static void statOpen(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbOpen(); + } + static void statSave(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbSave(); + } + static void statQuit(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbQuit(); + } + + static void statMidiBr(Fl_Widget* w, void* f){ + MainPanel::getInstance()->cbMidiBr(); + } + void cbMidiBr(); + static void statSceneNext(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbSceneNext(); + MainPanel::getInstance()->refresh(); + } + static void statScenePrev(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbScenePrev(); + MainPanel::getInstance()->refresh(); + } + static void statSceneDel(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbSceneDelete(); + MainPanel::getInstance()->refresh(); + } + static void statSceneLearn(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbSceneLearn(); + MainPanel::getInstance()->refresh(); + } + static void statFullscreen(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbFullscreen(); + MainPanel::getInstance()->refresh(); + } + static void statMirror(Fl_Widget* w,void* f){ + Controlar::getInstance()->cbFlip(); + MainPanel::getInstance()->refresh(); + } + void setMidiDevices(const std::vector<std::string>& midiDeviceNames, + const std::vector<bool>& midiDeviceOp); + inline void setCurrentScene(const int& sc){m_sceneOut->value(sc);} + + private: + MainPanel(); + Fl_Multi_Browser* m_midiBr; + Fl_Value_Output* m_sceneOut; +}; + +#endif diff --git a/src/ZonePanel.cpp b/src/ZonePanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f34e349619275231007e0015cebca23b0df8cbf8 --- /dev/null +++ b/src/ZonePanel.cpp @@ -0,0 +1,282 @@ +/*************************************************************************** + * ZonePanel.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "ZonePanel.hpp" +#include <FL/Fl_Button.H> +#include <FL/fl_draw.H> +#include <algorithm> +#include <iostream> +#include "Controlar.hpp" +#include "ZoneWidget.hpp" + +using namespace std; + +ZonePanel::ZonePanel(): FlipGroup(0, 0, 420, 200) { + int sep=5; + int butH=20; + int offY=sep; + int brW=200; + int brH=160; + int labW=60; + m_winsBr = new Fl_Hold_Browser(sep, offY, brW, brH, "window:"); + m_winsBr->align(FL_ALIGN_TOP); + m_winsBr->has_scrollbar(Fl_Browser_::VERTICAL_ALWAYS); + m_winsBr->callback(statWinBr, this); + addWid(m_winsBr); + + //transformations + Fl_Pack* radButs=new Fl_Pack(2*sep+brW, sep,(butH+sep)*4+50, + butH,"Transfo"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, labW, butH, "")); + for(int i=0; i<ZoneWidget::NB_TRANSFO; ++i) { + m_transfos[i]=ZoneWidget::ZONE_TRANSFO(i); + m_transfoButs[i]= new Fl_Radio_Button(0, 0, butH, butH, ""); + m_transfoButs[i]->callback(statZoneTransfo, &m_transfos[i]); + radButs->add(m_transfoButs[i]); + } + m_transfoButs[0]->label("@8->"); + m_transfoButs[1]->label("@6->"); + m_transfoButs[2]->label("@2->"); + m_transfoButs[3]->label("@4->"); + + //shapes + radButs=new Fl_Pack(2*sep+brW, sep*2+butH, 0, butH, "Shape"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, labW, butH, "")); + for(int i=0; i<ZoneWidget::NB_SHAPES; ++i) { + m_shapes[i]=ZoneWidget::ZONE_SHAPE(i); + m_shapeButs[i] = new Fl_Radio_Button(0, 0, butH, butH, ""); + m_shapeButs[i]->callback(statZoneShape, &m_shapes[i]); + radButs->add(m_shapeButs[i]); + } + m_shapeButs[0]->label("@-1square"); + m_shapeButs[1]->label("@-1circle"); + m_shapeButs[2]->label("@-1||"); + + //alpha + radButs=new Fl_Pack(2*sep+brW, sep+(sep+butH)*2, + (butH+sep)*3+60, butH, "Alpha"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, labW, butH, "")); + m_alphaSlider=new Fl_Slider(0, 0, (butH+sep)*3, butH, ""); + m_alphaSlider->callback(statZoneAlpha, this); + m_alphaSlider->type(FL_HORIZONTAL); + m_alphaSlider->bounds(0, 255); + radButs->add(m_alphaSlider); + + //colors + radButs=new Fl_Pack(2*sep+brW, sep+(sep+butH)*3, + (butH+sep)*2+50, butH, "Color"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, labW, butH, "")); + for(int i=0; i<ZoneWidget::NB_COLORS; ++i) { + m_colors[i]=ZoneWidget::ZONE_COLOR(i); + m_colorButs[i] = new Fl_Radio_Button(0,0, + butH, butH, ""); + m_colorButs[i]->callback(statZoneColor, &m_colors[i]); + radButs->add(m_colorButs[i]); + } + m_colorButs[0]->label("N"); + m_colorButs[1]->label("I"); + + //activity + radButs=new Fl_Pack(2*sep+brW, sep+(sep+butH)*4, + (butH+sep)*3+50, butH, "Activity"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, 60, butH, "")); + for(int i=0; i<ZoneWidget::NB_EFFECTS; ++i) { + m_inputs[i]=ZoneWidget::ZONE_INPUT_EFFECT(i); + m_inputButs[i] = new Fl_Radio_Button(0, 0, butH, butH, ""); + m_inputButs[i]->callback(statZoneInput, &m_inputs[i]); + radButs->add(m_inputButs[i]); + } + m_inputButs[0]->label("-"); + m_inputButs[1]->label("S"); + m_inputButs[2]->label("H"); + Fl_Button* learnBut = new Fl_Button(0, 0, 50, butH, "Learn"); + radButs->add(learnBut); + learnBut->callback(statZoneLearn,this); + + //content + radButs=new Fl_Pack(2*sep+brW, sep+(sep+butH)*6, + (butH+sep)*3+50, butH, "Content"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, 60, butH, "")); + for(int i=0; i<ZoneWidget::NB_CONTENTS; ++i) { + m_contents[i]=ZoneWidget::ZONE_CONTENT(i); + m_contentButs[i] = new Fl_Radio_Button(0, 0, butH*3, butH, ""); + m_contentButs[i]->callback(statZoneContent, &m_contents[i]); + radButs->add(m_contentButs[i]); + } + m_contentButs[0]->label("global"); + m_contentButs[1]->label("local"); + + //update + radButs=new Fl_Pack(2*sep+brW, sep+(sep+butH)*5, + (butH+sep)*3+50, butH, "Update"); + add(radButs); + radButs->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT); + radButs->type(Fl_Pack::HORIZONTAL); + radButs->spacing(sep); + radButs->box(FL_NO_BOX); + radButs->add(new Fl_Group(0, 0, 60, butH, "")); + for(int i=0; i<ZoneWidget::NB_UPDATES; ++i) { + m_updates[i]=ZoneWidget::ZONE_UPDATE(i); + m_updateButs[i] = new Fl_Radio_Button(0, 0, butH*3, butH, ""); + m_updateButs[i]->callback(statZoneUpdate, &m_updates[i]); + radButs->add(m_updateButs[i]); + } + m_updateButs[0]->label("all"); + m_updateButs[1]->label("current"); + + //delete + Fl_Button* delBut = new Fl_Button(sep, h()-sep-butH, 60, butH, "Delete"); + delBut->callback(statZoneDelete,this); + add(delBut); +} + +void ZonePanel::setWindowList(vector<CutWindow*>& wins) { + m_winsBr->clear(); + m_winsBr->add("none", NULL); + vector<CutWindow*>::iterator itWin = wins.begin(); + for(; itWin!=wins.end(); ++itWin) { + m_winsBr->add(((*itWin)->getName()).c_str(), (*itWin)); + } + m_winsBr->value(1); +} + +void ZonePanel::setZone(ZoneWidget* z) { + m_curZone=z; + + //retrieve values + if(m_curZone) { + CutWindow* cutW = m_curZone->getCutWin(); + if(cutW) { + string cutWN = cutW->getName(); + for(int i=1; i<=m_winsBr->size(); ++i) { + if(cutWN.compare(m_winsBr->text(i))==0) { + m_winsBr->value(i); + } + } + } + else { + m_winsBr->value(1); + } + + for(int i=0; i<int(ZoneWidget::NB_TRANSFO); ++i) { + m_transfoButs[i]->value(0); + } + m_transfoButs[int(m_curZone->getTransformation())]->value(1); + for(int i=0; i<int(ZoneWidget::NB_SHAPES); ++i) { + m_shapeButs[i]->value(0); + } + m_shapeButs[int(m_curZone->getShape())]->value(1); + for(int i=0; i<int(ZoneWidget::NB_COLORS); ++i) { + m_colorButs[i]->value(0); + } + m_colorButs[int(m_curZone->getColor())]->value(1); + for(int i=0; i<int(ZoneWidget::NB_EFFECTS); ++i) { + m_inputButs[i]->value(0); + } + m_inputButs[int(m_curZone->getEffect())]->value(1); + for(int i=0; i<int(ZoneWidget::NB_UPDATES); ++i) { + m_updateButs[i]->value(0); + } + m_updateButs[int(m_curZone->getZoneUpdate())]->value(1); + for(int i=0; i<int(ZoneWidget::NB_CONTENTS); ++i) { + m_contentButs[i]->value(0); + } + m_contentButs[int(m_curZone->getContent())]->value(1); + m_alphaSlider->value(m_curZone->getAlpha()); + } +} + +void ZonePanel::cbWinBr(Fl_Widget*) { + m_curZone->setCutWin((CutWindow*)(m_winsBr->data(m_winsBr->value()))); + refresh(); +} + +void ZonePanel::cbZoneTransfo(const ZoneWidget::ZONE_TRANSFO& trans) { + m_curZone->setTransformation(trans); + refresh(); +} + +void ZonePanel::cbZoneShape(const ZoneWidget::ZONE_SHAPE& shape) { + m_curZone->setShape(shape); + refresh(); +} + +void ZonePanel::cbZoneAlpha() { + m_curZone->setAlpha(m_alphaSlider->value()); + refresh(); +} + +void ZonePanel::cbZoneColor(const ZoneWidget::ZONE_COLOR& color) { + m_curZone->setColor(color); + refresh(); +} + +void ZonePanel::cbZoneInput(const ZoneWidget::ZONE_INPUT_EFFECT& inp) { + m_curZone->setInputEffect(inp); + refresh(); +} + +void ZonePanel::cbZoneUpdate(const ZoneWidget::ZONE_UPDATE& up) { + Controlar::getInstance()->cbZoneUpdate(up); + refresh(); +} + +void ZonePanel::cbZoneContent(const ZoneWidget::ZONE_CONTENT& cont) { + m_curZone->setContent(cont); + refresh(); +} + +ZonePanel* ZonePanel::getInstance() { + static ZonePanel instance; + return &instance; +} + diff --git a/src/ZonePanel.hpp b/src/ZonePanel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fb95307ee9bf4e25c7903841151911ea40afe02b --- /dev/null +++ b/src/ZonePanel.hpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * ZonePanel.hpp + * Part of ControllAR + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef ZonePanel_h +#define ZonePanel_h + +#include <string> +#include <map> +#include <vector> + +#include <FL/Fl_Choice.H> +#include <FL/Fl_Hold_Browser.H> +#include <FL/Fl_Radio_Round_Button.H> +#include <FL/Fl_Radio_Button.H> +#include <FL/Fl_Slider.H> +#include "Controlar.hpp" +#include "FlipGroup.hpp" +#include "CutWindow.hpp" + +class ZoneWidget; + +class ZonePanel: public FlipGroup { + public : + static ZonePanel* getInstance(); + ~ZonePanel(){}; + void setWindowList(std::vector<CutWindow*>& wins); + + static void statWinBr(Fl_Widget* w, void* f){ + ZonePanel *tmpf = static_cast<ZonePanel *>(f); + tmpf->cbWinBr(w); + } + void cbWinBr(Fl_Widget*); + + static void statZoneTransfo(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneTransfo(*(ZoneWidget::ZONE_TRANSFO*)(f)); + } + void cbZoneTransfo(const ZoneWidget::ZONE_TRANSFO&); + + static void statZoneShape(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneShape(*(ZoneWidget::ZONE_SHAPE*)(f)); + } + void cbZoneShape(const ZoneWidget::ZONE_SHAPE&); + + static void statZoneAlpha(Fl_Widget* w, void* f){ + ZonePanel::getInstance()->cbZoneAlpha(); + } + void cbZoneAlpha(); + + static void statZoneColor(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneColor(*(ZoneWidget::ZONE_COLOR*)(f)); + } + void cbZoneColor(const ZoneWidget::ZONE_COLOR&); + + static void statZoneInput(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneInput(*(ZoneWidget::ZONE_INPUT_EFFECT*)(f)); + } + void cbZoneInput(const ZoneWidget::ZONE_INPUT_EFFECT&); + + static void statZoneContent(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneContent(*(ZoneWidget::ZONE_CONTENT*)(f)); + } + void cbZoneContent(const ZoneWidget::ZONE_CONTENT&); + + static void statZoneUpdate(Fl_Widget* w, void* f){ + ZonePanel::getInstance() + ->cbZoneUpdate(*(ZoneWidget::ZONE_UPDATE*)(f)); + } + void cbZoneUpdate(const ZoneWidget::ZONE_UPDATE&); + + static void statZoneLearn(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneMidiLearn(); + } + + static void statZoneDelete(Fl_Widget* w, void* f){ + Controlar::getInstance()->cbZoneDelete(); + } + + void setZone(ZoneWidget*); + + private: + ZonePanel(); + Fl_Hold_Browser* m_winsBr; + ZoneWidget* m_curZone; + ZoneWidget::ZONE_TRANSFO m_transfos[ZoneWidget::NB_TRANSFO]; + Fl_Radio_Button* m_transfoButs[ZoneWidget::NB_TRANSFO]; + ZoneWidget::ZONE_SHAPE m_shapes[ZoneWidget::NB_SHAPES]; + Fl_Radio_Button* m_shapeButs[ZoneWidget::NB_SHAPES]; + ZoneWidget::ZONE_COLOR m_colors[ZoneWidget::NB_COLORS]; + Fl_Radio_Button* m_colorButs[ZoneWidget::NB_COLORS]; + ZoneWidget::ZONE_INPUT_EFFECT m_inputs[ZoneWidget::NB_EFFECTS]; + Fl_Radio_Button* m_inputButs[ZoneWidget::NB_EFFECTS]; + ZoneWidget::ZONE_CONTENT m_contents[ZoneWidget::NB_CONTENTS]; + Fl_Radio_Button* m_contentButs[ZoneWidget::NB_CONTENTS]; + ZoneWidget::ZONE_UPDATE m_updates[ZoneWidget::NB_UPDATES]; + Fl_Radio_Button* m_updateButs[ZoneWidget::NB_UPDATES]; + Fl_Slider* m_alphaSlider; +}; + +#endif diff --git a/src/ZoneWidget.cpp b/src/ZoneWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a6c542e0d7908bbd9e140c0d14c9ca4fe49b0273 --- /dev/null +++ b/src/ZoneWidget.cpp @@ -0,0 +1,1077 @@ +/*************************************************************************** + * ZoneWidget.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "ZoneWidget.hpp" +#include <iostream> +#include <sstream> +#include <cmath> + +#include "Controlar.hpp" + +using namespace std; + +const int ZoneWidget::GLOBAL_PROPS; + +ZoneWidget::ZoneWidget(int x, int y, int w, int h): Fl_Widget(x,y,w,h,"") { + box(FL_FLAT_BOX); + m_state=NORMAL_STATE; + m_imageData=NULL; + m_image=NULL; + m_coordImage=NULL; + recreateImage(); + m_dragging=false; + m_forcedDragging=false; + m_editing=false; + m_highlight=false; + m_scaleX=1; + m_scaleY=1; + m_midiLearning=false; + m_effectDelay.tv_sec=0; + m_effectDelay.tv_usec=250000; + m_hiddenByEffect=false; + m_effectDelayOver=true; + m_rateReached=false; + m_initialized=false; + m_visibleInScene=-1; + m_updateInScene=-1; + m_content=CONTENT_GLOBAL; + + m_curCutProps=ZoneWidget::GLOBAL_PROPS; + m_cutProps[m_curCutProps]=CutProps(); + m_currentScene=0; +} + +void ZoneWidget::copy(ZoneWidget* original) { + resize(original->x(), original->y(), original->w(), original->h()); + m_cutProps=original->m_cutProps; + m_curCutProps=original->m_curCutProps; + m_visibleInScene=original->m_visibleInScene; + m_updateInScene=original->m_updateInScene; + m_content = original->m_content; + recomputePixels(); +} + +ZoneWidget::~ZoneWidget() {} + +void ZoneWidget::setScene(const int& scene) { + if(m_updateInScene==m_currentScene && m_cutProps[m_curCutProps].m_ovWin) { + m_cutProps[m_curCutProps].m_ovWin->storeImage(m_currentScene); + } + if(m_content==CONTENT_LOCAL) { + m_curCutProps=scene; + } + else { + m_curCutProps=ZoneWidget::GLOBAL_PROPS; + } + m_currentScene=scene; + recomputePixels(); +} + +void ZoneWidget::setContent(const ZONE_CONTENT& cont) { + m_content=cont; + //set current scene to define current cut props + setScene(m_currentScene); + //copy props + if(m_content==CONTENT_LOCAL) { + //copy from global to local + m_cutProps[m_curCutProps]=m_cutProps[ZoneWidget::GLOBAL_PROPS]; + } + else { + //copy from local to global + m_cutProps[ZoneWidget::GLOBAL_PROPS]=m_cutProps[m_curCutProps]; + } +} + +void ZoneWidget::refreshCutWin() { + setCutWin(Controlar::getInstance() + ->getWindow(m_cutProps[m_curCutProps].m_ovWinName)); +} + +void ZoneWidget::setCutWin(CutWindow* ow) { + CutProps& props = m_cutProps[m_curCutProps]; + props.m_ovWin=ow; + if(props.m_ovWin) { //if the window exists + if(props.m_ovWinName.compare(props.m_ovWin->getName())==0) { //same name + //preserve coordinates if possible + props.m_winW = max(1, min(props.m_winW, + props.m_ovWin->getWidth())); + props.m_winH = max(1, min(props.m_winH, + props.m_ovWin->getHeight())); + props.m_winX = max(0, min(props.m_winX, + props.m_ovWin->getWidth()-props.m_winW)); + props.m_winY = max(0, min(props.m_winY, + props.m_ovWin->getHeight()-props.m_winH)); + m_nbPixPerRow=props.m_winW; + } + else { //different window + //reset position and size of the cut + props.m_ovWinName=props.m_ovWin->getName(); + props.m_winX=0; + props.m_winY=0; + props.m_winW = max(1, min(w(), props.m_ovWin->getWidth())); + props.m_winH = max(1, min(h(), props.m_ovWin->getHeight())); + m_nbPixPerRow=props.m_winW; + } + recomputePixels(); + } +} + +CutWindow* ZoneWidget::getCutWin() { + return m_cutProps[m_curCutProps].m_ovWin; +} + +void ZoneWidget::setInputEffect(const ZONE_INPUT_EFFECT& effect) { + ZONE_INPUT_EFFECT& meffect = m_cutProps[m_curCutProps].m_effect; + meffect=effect; + m_hiddenByEffect=(meffect==SHOW)?true:false; +} + +void ZoneWidget::setVisible(const int& vis) { + m_visibleInScene=vis; +} + +void ZoneWidget::setUpdate(const int& up) { + m_updateInScene=up; +} + +void ZoneWidget::processMidi(const int& midiType, + const int& midiChannel, + const int& midiControl, + const int& midiValue) { + if(m_midiLearning) { + m_midiType=midiType; + m_midiChannel=midiChannel; + m_midiControl=midiControl; + m_midiLearning=false; + } + ZONE_INPUT_EFFECT& meffect = m_cutProps[m_curCutProps].m_effect; + if(meffect!=NO_EFFECT) { + if(midiType==m_midiType + && midiChannel==m_midiChannel + && midiControl==m_midiControl) { + m_effectDelayOver=false; + gettimeofday(&m_effectStartTime, NULL); + switch(meffect) { + case SHOW:{ + m_hiddenByEffect=false; + }break; + case HIDE:{ + m_hiddenByEffect=true; + }break; + default:break; + } + } + } +} + +void ZoneWidget::drawZone(const int& currentScene) { + int nw=w()-OUTER*2; + int nh=h()-OUTER*2; + + CutProps& props = m_cutProps[m_curCutProps]; + + Controlar* cont = Controlar::getInstance(); + //process effect + if(props.m_effect!=NO_EFFECT && !m_effectDelayOver) { + timeval curTime, diffTime; + gettimeofday(&curTime, NULL); + timersub(&curTime, &m_effectStartTime, &diffTime); + if(timercmp(&diffTime, &m_effectDelay, >)) { + m_effectDelayOver=true; + switch(props.m_effect) { + case SHOW:{ + m_hiddenByEffect=true; + }break; + case HIDE:{ + m_hiddenByEffect=false; + }break; + default:break; + } + } + } + + //draw +#ifdef GL + if(m_visibleInScene<0 || m_visibleInScene==currentScene) { + glUniform1f(cont->getZoneFilledUniform(), 0.0); + glUniform1i(cont->getZoneStateUniform(), int(m_state)); + glUniform2f(cont->getZoneSizeUniform(), w(), h()); + glUniform2f(cont->getZonePosUniform(), x(), y()); + glUniform2f(cont->getCutSizeUniform(), nw, nh); + glUniform2f(cont->getCutPosUniform(), x()+OUTER, y()+OUTER); + glUniform1f(cont->getZoneAlphaUniform(), + m_hiddenByEffect?0.0:float(props.m_alpha)/255.0); + glUniform1f(cont->getZoneInvertUniform(), props.m_color==INVERT); + if(props.m_ovWin) { + glUniform1f(cont->getZoneFilledUniform(), 1.0); + //if using stored image from other scene + if(m_updateInScene>=0 && m_updateInScene!=currentScene) { + uchar* storedData=props.m_ovWin->retrieveImage(m_updateInScene); + if(storedData) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cont->getFirstTextureID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + m_nbPixPerRow, props.m_ovWin->getHeight(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, + storedData); + cont->setCurrentWindowName(""); + } + } + else if(cont->getCurrentWindowName() + .compare(props.m_ovWin->getName())!=0) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, cont->getFirstTextureID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + m_nbPixPerRow, props.m_ovWin->getHeight(), + 0, GL_RGBA, GL_UNSIGNED_BYTE, + props.m_ovWin->grabImage()); + cont->setCurrentWindowName(props.m_ovWin->getName()); + } + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, cont->getSecondTextureID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, + nw, nh, + 0, GL_RG, GL_FLOAT, + m_coordImage); + } + glDrawArrays(GL_TRIANGLES, 0, 6); + } + +#else + + //process drawing + if(props.m_ovWin) { + if(m_visibleInScene<0 || m_visibleInScene==currentScene) { + if(!m_hiddenByEffect && m_rateReached) { + m_image->uncache(); + m_ovWin->getPixels(props.m_winX, props.m_winY, + props.m_winW, props.m_winH, + m_srcIndices, m_srcCoords, m_destIndices, + props.m_alpha, props.m_color, m_imageData); + m_image->draw(x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h()); + } + switch(m_rate) { + case STATIC:m_rateReached=false;break; + default:m_rateReached=true;break; + } + } + } + else { + fl_rect(x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), FL_WHITE); + } + if(m_editing) { + fl_rectf(x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), FL_RED); + } + if(m_highlight) { + fl_rect(x(), cont->isFlipped()?cont->h()-y()-h():y(), + w(), h(), FL_RED); + } + +#endif +} + +void ZoneWidget::recomputePixels() { + CutProps& props = m_cutProps[m_curCutProps]; + + //if window attached, recompute correspondance between pixel coordinates + if(props.m_ovWin) { + + //get new pixperrow in window + m_nbPixPerRow = props.m_ovWin->computeNbPixPerRow(props.m_winW, + props.m_winH); + + //clear existing coordinates + m_srcIndices.clear(); + m_srcCoords.clear(); + m_destIndices.clear(); + vector<float> coord(2,0); + vector<float> start(2,0); + vector<bool> restart(2,false); + vector<float> stepY(2,0); + vector<float> stepX(2,0); + int nw=w()-OUTER*2; + int nh=h()-OUTER*2; + + //compute the pixels offset and steps depending on transformation + switch(props.m_transfo) { + case ROTATE_90 : { + start[0]=props.m_ovWin->needsOffset()?props.m_winX:0; + start[1]=props.m_ovWin->needsOffset()?props.m_winY+props.m_winH + :props.m_winH; + stepY[0]=float(props.m_winW)/float(nh); + restart[1]=true; + stepX[1]=-float(props.m_winH)/float(nw); + }break; + case ROTATE_180 : { + start[0]=props.m_ovWin->needsOffset()?props.m_winX+props.m_winW + :props.m_winW; + start[1]=props.m_ovWin->needsOffset()?props.m_winY+props.m_winH + :props.m_winH; + stepY[1]=-float(props.m_winH)/float(nh); + restart[0]=true; + stepX[0]=-float(props.m_winW)/float(nw); + }break; + case ROTATE_270 : { + start[0]=props.m_ovWin->needsOffset()?props.m_winX+props.m_winW + :props.m_winW; + start[1]=props.m_ovWin->needsOffset()?props.m_winY:0; + stepY[0]=-float(props.m_winW)/float(nh); + restart[1]=true; + stepX[1]=float(props.m_winH)/float(nw); + }break; + default : { + start[0]=props.m_ovWin->needsOffset()?props.m_winX:0; + start[1]=props.m_ovWin->needsOffset()?props.m_winY:0; + stepY[1]=float(props.m_winH)/float(nh); + restart[0]=true; + stepX[0]=float(props.m_winW)/float(nw); + }break; + } + + vector<int> intCoord(2,0); + coord[0]=start[0]; + coord[1]=start[1]; + + Controlar* cont = Controlar::getInstance(); + + //compute coordinates according to shape + switch(props.m_shape) { + case CIRCLE : { //circle shape + int radius = min(nw,nh); + for(int py = 0; py < nh; py++) { + for(int px = 0; px < nw; px++) { + float distX = px-nw/2; + float distY = py-nh/2; + float dist = sqrt(distX*distX + distY*distY); + if(dist>radius/4 && dist<radius/2) { //inside the circle + distX/=float(radius); + distY/=float(radius); + float angleY = (atan2(distY,distX)-M_PI/2.0) + / (2.0*M_PI)*float(nh); + if(angleY<0) { + angleY=nh+angleY; + } + float radiusX = + (dist-radius/4)/(radius/4)*float(nw); + intCoord[0] = + start[0]+stepY[0]*angleY+stepX[0]*radiusX; + intCoord[1] = + start[1]+stepY[1]*angleY+stepX[1]*radiusX; + } + else { + intCoord[0]=-1; intCoord[1]=-1; + } + m_srcCoords.push_back(intCoord); + int srcInd = (m_nbPixPerRow*intCoord[1] + +intCoord[0])*4; + if(props.m_ovWin->isBGR()) { + m_srcIndices.push_back(srcInd+2); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd); + } + else { + m_srcIndices.push_back(srcInd); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd+2); + } + m_srcIndices.push_back(srcInd+3); + for(int c=0; c<4; ++c) { + if(cont->isFlipped()) { + m_destIndices.push_back((nw*(nh-1-py)+px)*4+c); + } + else { + m_destIndices.push_back((nw*py+px)*4+c); + } + } + m_coordImage[(nw*py+px)*2]=float(intCoord[0]) + / float(props.m_ovWin->getWidth()); + m_coordImage[(nw*py+px)*2+1]=float(intCoord[1]) + / float(props.m_ovWin->getHeight()); + } + } + }break; + case FRAME : { //frame shape + int border=20; + for(int py = 0; py < nh; py++) { + coord[0]=restart[0]?start[0]:coord[0]; + coord[1]=restart[1]?start[1]:coord[1]; + for(int px = 0; px < nw; px++) { + if(px>border && px<nw-border + && py>border && py<nh-border) { + intCoord[0]=-1; intCoord[1]=-1; + } + else { + float nbSrcPix=0; + if(py<border) { + nbSrcPix = float(py)/float(border)*float(nh/2); + intCoord[0]=coord[0]; + intCoord[1]=start[1] + +nbSrcPix*stepX[1] + +nbSrcPix*stepY[1]; + } + else if(py>nh-border) { + nbSrcPix= float(nh) + -(float(nh-py) + /float(border) + *float(nh/2)); + intCoord[0]=coord[0]; + intCoord[1]=start[1] + +nbSrcPix*stepX[1] + +nbSrcPix*stepY[1]; + } + else if(px<border) { + nbSrcPix = float(px)/float(border)*float(nw/2); + intCoord[0]=start[0] + +nbSrcPix*stepX[0] + +nbSrcPix*stepY[0]; + intCoord[1]=coord[1]; + } + else { + nbSrcPix= float(nw) + -(float(nw-px) + /float(border) + *float(nw/2)); + intCoord[0]=start[0] + +nbSrcPix*stepX[0] + +nbSrcPix*stepY[0]; + intCoord[1]=coord[1]; + } + } + m_srcCoords.push_back(intCoord); + int srcInd = (m_nbPixPerRow*intCoord[1] + +intCoord[0])*4; + if(props.m_ovWin->isBGR()) { + m_srcIndices.push_back(srcInd+2); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd); + } + else { + m_srcIndices.push_back(srcInd); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd+2); + } + m_srcIndices.push_back(srcInd+3); + for(int c=0; c<4; ++c) { + if(cont->isFlipped()) { + m_destIndices.push_back((nw*(nh-1-py)+px)*4+c); + } + else { + m_destIndices.push_back((nw*py+px)*4+c); + } + } + coord[0]+=stepX[0]; + coord[1]+=stepX[1]; + m_coordImage[(nw*py+px)*2]=float(intCoord[0]) + / float(props.m_ovWin->getWidth()); + m_coordImage[(nw*py+px)*2+1]=float(intCoord[1]) + / float(props.m_ovWin->getHeight()); + } + coord[0]+=stepY[0]; + coord[1]+=stepY[1]; + } + }break; + default: { //box shape + for(int py = 0; py < nh; py++) { + coord[0]=restart[0]?start[0]:coord[0]; + coord[1]=restart[1]?start[1]:coord[1]; + for(int px = 0; px < nw; px++) { + intCoord[0]=coord[0]; intCoord[1]=coord[1]; + m_srcCoords.push_back(intCoord); + int srcInd = (m_nbPixPerRow*intCoord[1] + +intCoord[0])*4; + if(props.m_ovWin->isBGR()) { + m_srcIndices.push_back(srcInd+2); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd); + } + else { + m_srcIndices.push_back(srcInd); + m_srcIndices.push_back(srcInd+1); + m_srcIndices.push_back(srcInd+2); + } + m_srcIndices.push_back(srcInd+3); + for(int c=0; c<4; ++c) { + if(cont->isFlipped()) { + m_destIndices.push_back((nw*(nh-1-py)+px)*4+c); + } + else { + m_destIndices.push_back((nw*py+px)*4+c); + } + } + coord[0]+=stepX[0]; + coord[1]+=stepX[1]; + m_coordImage[(nw*py+px)*2]=float(intCoord[0]) + / float(props.m_ovWin->getWidth()); + m_coordImage[(nw*py+px)*2+1]=float(intCoord[1]) + / float(props.m_ovWin->getHeight()); + } + coord[0]+=stepY[0]; + coord[1]+=stepY[1]; + } + }break; + } + m_scaleX=(stepY[0]!=0)?abs(stepY[0]):abs(stepX[0]); + m_scaleY=(stepY[1]!=0)?abs(stepY[1]):abs(stepX[1]); + } +} + +void ZoneWidget::recreateImage() { + int nw=w()-OUTER*2; + int nh=h()-OUTER*2; + if(m_imageData) { + delete [] m_imageData; + } + m_imageData = new uchar[nw*nh*4]; + if(m_image) { + delete m_image; + } + m_image = new Fl_RGB_Image(m_imageData, nw, nh, 4); + + if(m_coordImage) { + delete m_coordImage; + } + m_coordImage = new float[nw*nh*2]; +} + +int ZoneWidget::handle(int event) { + CutProps& props = m_cutProps[m_curCutProps]; + int res=0; + switch(event) { + case FL_MOUSEWHEEL : { + Fl_Widget* wid = Fl::belowmouse(); + if(parent()!=NULL) { + parent()->insert(*wid,0); + } + res=1; + }break; + case FL_PUSH:{ + if(Fl::event_inside(this)) { + m_forcedDragging=false; + switch(Fl::event_button()) { + case FL_RIGHT_MOUSE: { + do_callback(); + res=1; + }break; + case FL_LEFT_MOUSE: { + //if(Fl::event_shift()) { //move/resize zone + if(m_state==MOVE_ZONE || m_state==RESIZE_ZONE) { + startDraggingZone(); + m_editing=true; + } + //else if(props.m_ovWin) { //move/resize window + else if(props.m_ovWin) { //move/resize window + m_editing=true; + switch(props.m_transfo) { + case ROTATE_90: { + m_dragStartX=props.m_winY; + m_dragStartY=props.m_winX; + m_dragX=Fl::event_x(); + m_dragY=Fl::event_y(); + m_dragW=props.m_winW+Fl::event_y(); + m_dragH=props.m_winH-Fl::event_x(); + }break; + case ROTATE_180: { + m_dragStartX=props.m_winX; + m_dragStartY=props.m_winY; + m_dragX=Fl::event_x(); + m_dragY=Fl::event_y(); + m_dragW=props.m_winW-Fl::event_x(); + m_dragH=props.m_winH-Fl::event_y(); + }break; + case ROTATE_270: { + m_dragStartX=props.m_winY; + m_dragStartY=props.m_winX; + m_dragX=Fl::event_x(); + m_dragY=Fl::event_y(); + m_dragW=props.m_winW-Fl::event_y(); + m_dragH=props.m_winH+Fl::event_x(); + }break; + default: { + m_dragStartX=props.m_winX; + m_dragStartY=props.m_winY; + m_dragX=Fl::event_x(); + m_dragY=Fl::event_y(); + m_dragW=props.m_winW+Fl::event_x(); + m_dragH=props.m_winH+Fl::event_y(); + }break; + } + } + m_dragging=true; + res=1; + }break; + default:break; + } + } + }break; + case FL_DRAG: { + if(m_dragging) { + if(Fl::event_button()==FL_LEFT_MOUSE) { +/* + if(Fl::event_shift()) { //move/resize zone + m_editing=true; + if(Fl::event_command()) { + size(m_dragW+Fl::event_x(), m_dragH+Fl::event_y()); + } + else { + position(m_dragX+Fl::event_x(), + m_dragY+Fl::event_y()); + } + } +*/ + if(m_state==MOVE_ZONE) { + m_editing=true; + position(m_dragX+Fl::event_x(), + m_dragY+Fl::event_y()); + } + else if(m_state==RESIZE_ZONE) { + m_editing=true; + size(max(float(OUTER*2), m_dragW+Fl::event_x()), + max(float(OUTER*2), m_dragH+Fl::event_y())); + } + else if(props.m_ovWin) { //move/resize window + int transDW, transDH, transDX, transDY; + switch(props.m_transfo) { + case ROTATE_90: { + transDW=m_dragW-Fl::event_y(); + transDH=m_dragH+Fl::event_x(); + transDX=m_dragStartY + - m_scaleX*(Fl::event_y()-m_dragY); + transDY=m_dragStartX + + m_scaleY*(Fl::event_x()-m_dragX); + }break; + case ROTATE_180: { + transDW=m_dragW+Fl::event_x(); + transDH=m_dragH+Fl::event_y(); + transDX=m_dragStartX + + m_scaleX*(Fl::event_x()-m_dragX); + transDY=m_dragStartY + + m_scaleY*(Fl::event_y()-m_dragY); + }break; + case ROTATE_270: { + transDW=m_dragW+Fl::event_y(); + transDH=m_dragH-Fl::event_x(); + transDX=m_dragStartY + + m_scaleX*(Fl::event_y()-m_dragY); + transDY=m_dragStartX + - m_scaleY*(Fl::event_x()-m_dragX); + }break; + default: { + transDW=m_dragW-Fl::event_x(); + transDH=m_dragH-Fl::event_y(); + transDX=m_dragStartX + - m_scaleX*(Fl::event_x()-m_dragX); + transDY=m_dragStartY + - m_scaleY*(Fl::event_y()-m_dragY); + }break; + } + //if(Fl::event_command()) { + if(m_state==RESIZE_CUT) { + props.m_winW = max(1, min(transDW, + props.m_ovWin->getWidth())); + props.m_winH = max(1, min(transDH, + props.m_ovWin->getHeight())); + props.m_winX = max(0, min(props.m_winX, + props.m_ovWin->getWidth() + - props.m_winW)); + props.m_winY = max(0, min(props.m_winY, + props.m_ovWin->getHeight() + - props.m_winH)); + } + else { + props.m_winX = max(0, min(transDX, + props.m_ovWin->getWidth() + - props.m_winW)); + props.m_winY = max(0, min(transDY, + props.m_ovWin->getHeight() + - props.m_winH)); + } + recomputePixels(); + } + res=1; + } + } + }break; + case FL_RELEASE: { + m_editing=false; + res=1; + }break; + case FL_SHORTCUT: { + if(m_highlight && Fl::event_key()==FL_Shift_L) { + m_editing=true; + return 1; + } + else { + switch(Fl::event_key()) { + case 'd': { + m_forcedDragging=false; + Controlar::getInstance()->startDuplicating(this); + res=1; + }break; + case 'a': { + props.m_alpha=(props.m_alpha<255)?props.m_alpha+10:255; + res=1; + }break; + case 'q': { + props.m_alpha=(props.m_alpha>0)?props.m_alpha-10:0; + res=1; + }break; + case 's' : { + props.m_shape = ZONE_SHAPE((props.m_shape+1)%NB_SHAPES); + recomputePixels(); + res=1; + }break; + case 't' : { + props.m_transfo + = ZONE_TRANSFO((props.m_transfo+1)%NB_TRANSFO); + recomputePixels(); + res=1; + }break; + case 'c' : { + props.m_color= ZONE_COLOR((props.m_color+1)%NB_COLORS); + res=1; + }break; + case 'e' : { + setInputEffect(ZONE_INPUT_EFFECT((props.m_effect+1) + %NB_EFFECTS)); + res=1; + }break; + case FL_Left : + case FL_Right : + case FL_Down : + case FL_Up : { + int transDX=props.m_winX; + int transDY=props.m_winY; + int transDW=props.m_ovWin->getWidth(); + int transDH=props.m_ovWin->getHeight(); + if((Fl::event_key()==FL_Left && props.m_transfo==NONE) + || (Fl::event_key()==FL_Right + && props.m_transfo==ROTATE_180) + || (Fl::event_key()==FL_Down + && props.m_transfo==ROTATE_270) + || (Fl::event_key()==FL_Up + && props.m_transfo==ROTATE_90) ) { + transDX-=10*m_scaleX; + transDW-=10*m_scaleX; + } + else if((Fl::event_key()==FL_Right + && props.m_transfo==NONE) + || (Fl::event_key()==FL_Left + && props.m_transfo==ROTATE_180) + || (Fl::event_key()==FL_Up + && props.m_transfo==ROTATE_270) + || (Fl::event_key()==FL_Down + && props.m_transfo==ROTATE_90) ) { + transDX+=10*m_scaleX; + transDW+=10*m_scaleX; + } + else if((Fl::event_key()==FL_Down + && props.m_transfo==NONE) + || (Fl::event_key()==FL_Up + && props.m_transfo==ROTATE_180) + || (Fl::event_key()==FL_Right + && props.m_transfo==ROTATE_270) + || (Fl::event_key()==FL_Left + && props.m_transfo==ROTATE_90) ) { + transDY+=10*m_scaleY; + transDH+=10*m_scaleY; + } + else if((Fl::event_key()==FL_Up + && props.m_transfo==NONE) + || (Fl::event_key()==FL_Down + && props.m_transfo==ROTATE_180) + || (Fl::event_key()==FL_Left + && props.m_transfo==ROTATE_270) + || (Fl::event_key()==FL_Right + && props.m_transfo==ROTATE_90) ) { + transDY-=10*m_scaleY; + transDH-=10*m_scaleY; + } + if(Fl::event_command()) { + props.m_winW = max(1, min(transDW, + props.m_ovWin->getWidth())); + props.m_winH = max(1, min(transDH, + props.m_ovWin->getHeight())); + props.m_winX = max(0, min(props.m_winX, + props.m_ovWin->getWidth() + - props.m_winW)); + props.m_winY = max(0, min(props.m_winY, + props.m_ovWin->getHeight() + - props.m_winH)); + } + else { + props.m_winX = max(0, min(transDX, + props.m_ovWin->getWidth() + - props.m_winW)); + props.m_winY = max(0, min(transDY, + props.m_ovWin->getHeight() + - props.m_winH)); + } + recomputePixels(); + res=1; + }break; + } + } + }break; + case FL_KEYUP: { + if(m_highlight && Fl::event_key()==FL_Shift_L) { + m_editing=false; + res=1; + } + }break; + case FL_MOVE:{ + if(!m_editing) { + int inPosX = Fl::event_x()-x(); + int inPosY = Fl::event_y()-y(); + if(inPosX>OUTER && inPosY>OUTER + && inPosX<w()-OUTER && inPosY<h()-OUTER) { + if(sqrt(pow(inPosX-w(),2)+pow(inPosY-h(),2))<OUTER*3) { + m_state=RESIZE_CUT; + } + else { + m_state=MOVE_CUT; + } + } + else { + if(sqrt(pow(inPosX-w(),2)+pow(inPosY-h(),2))<OUTER) { + m_state=RESIZE_ZONE; + } + else { + m_state=MOVE_ZONE; + } + } + } + if(m_forcedDragging) { + position(m_dragX+Fl::event_x(), + m_dragY+Fl::event_y()); + } + res=1; + }break; + case FL_ENTER: { + m_highlight=true; + m_editing=false; + res=1; + }break; + case FL_LEAVE: { + m_state=NORMAL_STATE; + m_highlight=false; + m_editing=false; + res=1; + }break; + default:break; + } + if(res==1) { + parent()->redraw(); + } + return res; +} + +void ZoneWidget::forceDraggingZone() { + startDraggingZone(); + m_forcedDragging=true; +} + +void ZoneWidget::startDraggingZone() { + m_dragX=x()-Fl::event_x(); + m_dragY=y()-Fl::event_y(); + m_dragW=w()-Fl::event_x(); + m_dragH=h()-Fl::event_y(); +} + +void ZoneWidget::resize(int nx, int ny, int nw, int nh) { + bool recImg=false; + if(nw!=w() || nh!=h()) { + recImg=true; + } + Fl_Widget::resize(nx, ny, nw, nh); + if(recImg) { + recreateImage(); + recomputePixels(); + } +} + +void ZoneWidget::load(xmlNodePtr zoneNode) { + CutProps& props = m_cutProps[m_curCutProps]; + + char* value = NULL; + + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"win_name"); + if(value!=NULL) { + props.m_ovWinName = string(value); + props.m_ovWin = Controlar::getInstance()->getWindow(props.m_ovWinName); + } + + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"win_x"); + if(value!=NULL) { + props.m_winX = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"win_y"); + if(value!=NULL) { + props.m_winY = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"win_w"); + if(value!=NULL) { + props.m_winW = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"win_h"); + if(value!=NULL) { + props.m_winH = atoi(value); + } + int x=0,y=0,w=20,h=20; + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"zone_x"); + if(value!=NULL) { + x = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"zone_y"); + if(value!=NULL) { + y = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"zone_w"); + if(value!=NULL) { + w = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"zone_h"); + if(value!=NULL) { + h = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"alpha"); + if(value!=NULL) { + props.m_alpha = atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"transform"); + if(value!=NULL) { + props.m_transfo = ZONE_TRANSFO(atoi(value)); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"shape"); + if(value!=NULL) { + props.m_shape = ZONE_SHAPE(atoi(value)); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"effect"); + if(value!=NULL) { + setInputEffect(ZONE_INPUT_EFFECT(atoi(value))); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"color"); + if(value!=NULL) { + setColor(ZONE_COLOR(atoi(value))); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"midi_type"); + if(value!=NULL) { + m_midiType=atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"midi_channel"); + if(value!=NULL) { + m_midiChannel=atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"midi_control"); + if(value!=NULL) { + m_midiControl=atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"visible"); + if(value!=NULL) { + m_visibleInScene=atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"update"); + if(value!=NULL) { + m_updateInScene=atoi(value); + } + value = (char*)xmlGetProp(zoneNode,(xmlChar*)"content"); + if(value!=NULL) { + m_content=ZONE_CONTENT(atoi(value)); + } + resize(x,y,w,h); +} + +void ZoneWidget::save(xmlNodePtr parentNode) { + CutProps& props = m_cutProps[m_curCutProps]; + xmlNodePtr newNode = xmlNewChild(parentNode, NULL, + BAD_CAST "Zone", NULL); + if(props.m_ovWin) { + xmlNewProp(newNode, BAD_CAST "win_name", + BAD_CAST props.m_ovWinName.c_str()); + } + ostringstream oss1, oss2, oss3, oss4, oss5, + oss6, oss7, oss8, oss9, oss10, + oss11, oss12, oss13, oss14, oss15, + oss16, oss17, oss18, oss19; + oss1<<props.m_winX; + xmlNewProp(newNode, BAD_CAST "win_x", + BAD_CAST oss1.str().c_str()); + oss2<<props.m_winY; + xmlNewProp(newNode, BAD_CAST "win_y", + BAD_CAST oss2.str().c_str()); + oss3<<props.m_winW; + xmlNewProp(newNode, BAD_CAST "win_w", + BAD_CAST oss3.str().c_str()); + oss4<<props.m_winH; + xmlNewProp(newNode, BAD_CAST "win_h", + BAD_CAST oss4.str().c_str()); + oss5<<x(); + xmlNewProp(newNode, BAD_CAST "zone_x", + BAD_CAST oss5.str().c_str()); + oss6<<y(); + xmlNewProp(newNode, BAD_CAST "zone_y", + BAD_CAST oss6.str().c_str()); + oss7<<w(); + xmlNewProp(newNode, BAD_CAST "zone_w", + BAD_CAST oss7.str().c_str()); + oss8<<h(); + xmlNewProp(newNode, BAD_CAST "zone_h", + BAD_CAST oss8.str().c_str()); + oss9<<props.m_alpha; + xmlNewProp(newNode, BAD_CAST "alpha", + BAD_CAST oss9.str().c_str()); + oss10<<props.m_transfo; + xmlNewProp(newNode, BAD_CAST "transform", + BAD_CAST oss10.str().c_str()); + oss11<<props.m_shape; + xmlNewProp(newNode, BAD_CAST "shape", + BAD_CAST oss11.str().c_str()); + oss12<<props.m_effect; + xmlNewProp(newNode, BAD_CAST "effect", + BAD_CAST oss12.str().c_str()); + oss13<<m_midiType; + xmlNewProp(newNode, BAD_CAST "midi_type", + BAD_CAST oss13.str().c_str()); + oss14<<m_midiChannel; + xmlNewProp(newNode, BAD_CAST "midi_channel", + BAD_CAST oss14.str().c_str()); + oss15<<m_midiControl; + xmlNewProp(newNode, BAD_CAST "midi_control", + BAD_CAST oss15.str().c_str()); + oss16<<props.m_color; + xmlNewProp(newNode, BAD_CAST "color", + BAD_CAST oss16.str().c_str()); + oss17<<m_visibleInScene; + xmlNewProp(newNode, BAD_CAST "visible", + BAD_CAST oss17.str().c_str()); + oss18<<m_updateInScene; + xmlNewProp(newNode, BAD_CAST "update", + BAD_CAST oss18.str().c_str()); + oss19<<m_content; + xmlNewProp(newNode, BAD_CAST "content", + BAD_CAST oss19.str().c_str()); +} + + diff --git a/src/ZoneWidget.hpp b/src/ZoneWidget.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ebbbe36c865dc9135dd2ce003653728ac61293a0 --- /dev/null +++ b/src/ZoneWidget.hpp @@ -0,0 +1,195 @@ +/*************************************************************************** + * ZoneWidget.hpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ZoneWidget_h +#define ZoneWidget_h + +#include <FL/Fl.H> +#include <FL/Fl_Widget.H> +#include <FL/fl_draw.H> +#include <GL/glew.h> +#include <FL/gl.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> + +#include <sys/time.h> +#include <string> +#include <vector> +#include <map> + +class CutWindow; + +class ZoneWidget : public Fl_Widget { + + public: + enum ZONE_TRANSFO{NONE, ROTATE_90, ROTATE_180, ROTATE_270, NB_TRANSFO}; + enum ZONE_SHAPE{BOX, CIRCLE, FRAME, NB_SHAPES}; + enum ZONE_INPUT_EFFECT{NO_EFFECT, SHOW, HIDE, NB_EFFECTS}; + enum ZONE_COLOR{NORMAL, INVERT, NB_COLORS}; + enum ZONE_VISIBLE{VISIBLE_ALL, VISIBLE_CURRENT}; + enum ZONE_UPDATE{UPDATE_ALL, UPDATE_CURRENT, NB_UPDATES}; + enum ZONE_CONTENT{CONTENT_GLOBAL, CONTENT_LOCAL, NB_CONTENTS}; + enum ZONE_STATE{NORMAL_STATE,MOVE_CUT,RESIZE_CUT,MOVE_ZONE,RESIZE_ZONE}; + + static const int INNER=20; + static const int OUTER=20; + + struct CutProps { + CutProps(): m_ovWin(NULL), m_ovWinName(""), m_winX(0), m_winY(0), + m_winW(0), m_winH(0), m_alpha(255), m_transfo(NONE), + m_color(NORMAL), m_shape(BOX), m_effect(NO_EFFECT){} + CutWindow* m_ovWin; + std::string m_ovWinName; + int m_winX, m_winY; + int m_winW, m_winH; + int m_alpha; + ZONE_TRANSFO m_transfo; + ZONE_COLOR m_color; + ZONE_SHAPE m_shape; + ZONE_INPUT_EFFECT m_effect; + }; + + public: + ZoneWidget(int x=0, int y=0, int w=10, int h=10); + ~ZoneWidget(); + void copy(ZoneWidget*); + inline void draw(){drawZone(-1);} + void drawZone(const int&); + int handle(int event); + void setCutWin(CutWindow* ow); + CutWindow* getCutWin(); + void save(xmlNodePtr parentNode); + void load(xmlNodePtr); + void resize(int x, int y, int w, int h); + inline void setID(const unsigned int& id){m_id=id;} + inline const unsigned int& getID(){return m_id;} + inline void setAlpha(const int& alpha) { + m_cutProps[m_curCutProps].m_alpha=alpha; + recomputePixels(); + } + inline const int& getAlpha(){return m_cutProps[m_curCutProps].m_alpha;} + inline void setTransformation(const ZONE_TRANSFO& transfo) { + m_cutProps[m_curCutProps].m_transfo=transfo; + recomputePixels(); + } + inline const ZONE_TRANSFO& getTransformation() { + return m_cutProps[m_curCutProps].m_transfo; + } + inline void setShape(const ZONE_SHAPE& shape) { + m_cutProps[m_curCutProps].m_shape=shape; + recomputePixels(); + } + inline const ZONE_SHAPE& getShape() { + return m_cutProps[m_curCutProps].m_shape; + } + void setScene(const int& scene); + void setInputEffect(const ZONE_INPUT_EFFECT& effect); + inline const ZONE_INPUT_EFFECT& getEffect() { + return m_cutProps[m_curCutProps].m_effect; + } + void setColor(const ZONE_COLOR& color) { + m_cutProps[m_curCutProps].m_color=color; + } + inline const ZONE_COLOR& getColor() { + return m_cutProps[m_curCutProps].m_color; + } + void setVisible(const int& vis=-1); + void setUpdate(const int& up=-1); + inline int getUpdate(){return m_updateInScene;} + inline ZONE_UPDATE getZoneUpdate() { + return ZONE_UPDATE(m_updateInScene>=0); + } + void setContent(const ZONE_CONTENT& cont); + inline const ZONE_CONTENT& getContent() { + return m_content; + } + inline void learnMidi(){m_midiLearning=true;} + void refreshCutWin(); + void startDraggingZone(); + void forceDraggingZone(); + inline void setRateReached(){m_rateReached=true;} + + void processMidi(const int& midiType, + const int& midiChannel, + const int& midiControl, + const int& midiValue); + + void recomputePixels(); + + private: + void recreateImage(); + + private: + unsigned int m_id; +// CutWindow* m_ovWin; +// std::string m_ovWinName; + Fl_RGB_Image* m_image; + uchar* m_imageData; +/* + int m_winX, m_winY; + int m_winW, m_winH; +*/ + std::map<int, CutProps> m_cutProps; + static const int GLOBAL_PROPS=-1; + int m_curCutProps; + int m_currentScene; + + ZONE_STATE m_state; + bool m_editing; + bool m_highlight; + bool m_dragging; + bool m_forcedDragging; + float m_dragX, m_dragY; + float m_dragW, m_dragH; + float m_dragStartX, m_dragStartY; + float m_scaleX, m_scaleY; + int m_nbPixPerRow; + std::vector<int> m_srcIndices; + std::vector<std::vector<int> > m_srcCoords; + std::vector<int> m_destIndices; +/* + int m_alpha; + ZONE_TRANSFO m_transfo; + ZONE_SHAPE m_shape; +*/ + bool m_rateReached; +// ZONE_COLOR m_color; + ZONE_CONTENT m_content; + bool m_midiLearning; + int m_midiControl; + int m_midiChannel; + int m_midiType; +// ZONE_INPUT_EFFECT m_effect; + timeval m_effectDelay; + timeval m_effectStartTime; + bool m_hiddenByEffect; + bool m_effectDelayOver; + bool m_initialized; + float* m_coordImage; + int m_visibleInScene; + int m_updateInScene; +}; + + +#endif + diff --git a/src/mac/MacWindow.cpp b/src/mac/MacWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c30444a69c113fb2d4379d4390cfbc2d105196a --- /dev/null +++ b/src/mac/MacWindow.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * MacWindow.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "MacWindow.hpp" +#include <iostream> + +#include "../Controlar.hpp" + +using namespace std; + +MacWindow::MacWindow(CutWindowsManager* man, CGWindowID id): CutWindow(man) { + CGRect rect = CGRectMake(0, 0, 100, 100); + m_idCArray[0] = id; + m_idArray = CFArrayCreate(NULL, (const void **) m_idCArray, 1, NULL); + CFArrayRef descArray = CGWindowListCreateDescriptionFromArray(m_idArray); + if(CFArrayGetCount(descArray)>0) { + CFDictionaryRef description = + (CFDictionaryRef) CFArrayGetValueAtIndex(descArray, 0); + if (CFDictionaryContainsKey(description, kCGWindowBounds)) { + CFDictionaryRef bounds = + (CFDictionaryRef) CFDictionaryGetValue(description, + kCGWindowBounds); + if (bounds) { + CGRectMakeWithDictionaryRepresentation(bounds, &rect); + } + } + } + CFRelease(descArray); + + m_posX = rect.origin.x; + m_posY = rect.origin.y; + m_width = rect.size.width; + m_height = rect.size.height; + m_offsetX = m_posX; + m_offsetY = m_posY; + m_pixPerRow = m_width; + m_isBGR=true; + m_needsOffset=true; + m_pixPerRow=m_width; + m_grabbed=false; + m_defaultImgData = new uchar[m_width*m_height*3]; +} + +MacWindow::~MacWindow() { + CFRelease(m_idArray); +} + +int MacWindow::computeNbPixPerRow(const int& srcW, const int& srcH) { + CGImageRef img = CGWindowListCreateImageFromArray(CGRectNull, + m_idArray, + kCGWindowImageBoundsIgnoreFraming); + + int nb = CGImageGetBytesPerRow(img)/4; + m_width=CGImageGetWidth(img)*Controlar::getInstance()->getDisplayScale(); + m_height=CGImageGetHeight(img)*Controlar::getInstance()->getDisplayScale(); + CFRelease(img); + return nb; +} + +void MacWindow::getPixels(const int& srcX, const int& srcY, + const int& srcW, const int& srcH, + const vector<int>& srcIndices, + const vector<vector<int> >& srcCoords, + const vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg) { + //CGRect rect = CGRectMake(m_posX+srcX, m_posY+srcY, srcW, srcH); + //CGImageRef img = CGWindowListCreateImageFromArray(rect, + CGImageRef img = CGWindowListCreateImageFromArray(CGRectNull, + m_idArray, + kCGWindowImageBoundsIgnoreFraming); + if(img!=NULL) { + CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(img)); + uchar* buf = (uchar*)CFDataGetBytePtr(data); + vector<int>::const_iterator itId = srcIndices.begin(); + vector<vector<int> >::const_iterator itCo = srcCoords.begin(); + vector<int>::const_iterator itDe = destIndices.begin(); + for(; itCo!=srcCoords.end(); ++itCo) { + if((*itCo)[0]>=0) { + //copy rgb + for(int c=0; c<3; ++c) { + destImg[(*itDe)] = (color==ZoneWidget::NORMAL) + ?buf[(*itId)] + :255-buf[(*itId)]; + ++itId; + ++itDe; + } + //copy alpha + destImg[(*itDe)] = alpha; + ++itId; + ++itDe; + } + else { //black + for(int c=0; c<4; ++c) { + destImg[(*itDe)] = 0; + ++itId; + ++itDe; + } + } + } + //release CG stuff + CFRelease(data); + CFRelease(img); + } + else { + cout<<"Error getting image of window "<<m_name<<endl; + } +} + +void MacWindow::releaseImage() { + if(m_grabbed) { + CFRelease(m_data); + CFRelease(m_img); + m_grabbed=false; + } +} + +uchar* MacWindow::grabImage() { + if(!m_grabbed) { + m_img = CGWindowListCreateImageFromArray(CGRectNull, + m_idArray, + kCGWindowImageBoundsIgnoreFraming); + if(m_img) { + m_data = CGDataProviderCopyData(CGImageGetDataProvider(m_img)); + m_imgData = (uchar*)CFDataGetBytePtr(m_data); + m_grabbed=true; + } + else { + m_imgData=m_defaultImgData; + } + } + return m_imgData; +} + +void MacWindow::storeImage(const int& id) { + int size = m_pixPerRow*m_height*4; + if(m_storedImgData.find(id)!=m_storedImgData.end()) { + delete [] m_storedImgData[id]; + } + m_storedImgData[id] = new uchar[size]; + memcpy(m_storedImgData[id], m_imgData, size); +} + +uchar* MacWindow::retrieveImage(const int& id) { + map<int, uchar*>::iterator itSt = m_storedImgData.find(id); + if(itSt!=m_storedImgData.end()) { + return itSt->second; + } + return NULL; +} + + diff --git a/src/mac/MacWindow.hpp b/src/mac/MacWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9ace8e964e68a081744ae62b586c5521717e972a --- /dev/null +++ b/src/mac/MacWindow.hpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * MacWindow.hpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef MacWindow_h +#define MacWindow_h + +#include <vector> +#include <string> + +#include "../CutWindow.hpp" +#import <ApplicationServices/ApplicationServices.h> +//#import <Cocoa/Cocoa.h> +//#import <QuartzCore/QuartzCore.h> +//#import <AppKit/AppKit.h> + +class MacWindow: public CutWindow { + public : + MacWindow(CutWindowsManager* man, CGWindowID winID); + virtual ~MacWindow(); + virtual void getPixels(const int& x, const int& y, + const int& sx, const int& sy, + const std::vector<int>& srcIndices, + const std::vector<std::vector<int> >& srcCoords, + const std::vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg); + int computeNbPixPerRow(const int& srcW, const int& srcH); + virtual uchar* grabImage(); + virtual void releaseImage(); + virtual void storeImage(const int& id); + virtual uchar* retrieveImage(const int& id); + + protected: + CGWindowID m_idCArray[1]; + CFArrayRef m_idArray; + CGImageRef m_img; + CFDataRef m_data; +}; + +#endif + diff --git a/src/mac/MacWindowsManager.cpp b/src/mac/MacWindowsManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43d09d06c16f43f041ced79eb8f76f7ed6d36939 --- /dev/null +++ b/src/mac/MacWindowsManager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * MacWindowsManager.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Relies on code from: + https://github.com/JoelSjogren/window-copy/blob/master/windowutils.cpp +*/ + +#include "MacWindowsManager.hpp" +#include <iostream> +#include <stdlib.h> + +#include "../Controlar.hpp" + +using namespace std; + +MacWindowsManager::MacWindowsManager(): CutWindowsManager() {} + +MacWindowsManager::~MacWindowsManager() {} + +void MacWindowsManager::updateWindowsList() { + map<unsigned int, CutWindow*> eraseWindows = m_windowMap; + m_windowList.clear(); + CFArrayRef windowArray = + CGWindowListCreate(kCGWindowListExcludeDesktopElements + |kCGWindowListOptionOnScreenOnly, kCGNullWindowID); + CFArrayRef descArray = CGWindowListCreateDescriptionFromArray(windowArray); + if(descArray!=NULL) { + for(int i=0; i<CFArrayGetCount(descArray); ++i) { + CFDictionaryRef win = + (CFDictionaryRef)CFArrayGetValueAtIndex(descArray,i); + CFStringRef name = + (CFStringRef) CFDictionaryGetValue(win, kCGWindowName); + if(name!=NULL) { + if(CFStringGetLength(name)>0) { + char tempStr[128]; + CFStringGetCString(name,tempStr,128,kCFStringEncodingUTF8); + string nameStr(tempStr); + unsigned int winID=(uintptr_t) + CFArrayGetValueAtIndex(windowArray,i); + if(m_windowMap.find(winID)==m_windowMap.end()) { + MacWindow* newWin + = new MacWindow(this, + (CGWindowID)(uintptr_t) + CFArrayGetValueAtIndex(windowArray,i)); + newWin->setName(nameStr); + newWin->setWinID(winID); + m_windowMap[winID]=newWin; + } + m_windowList.push_back(m_windowMap[winID]); + eraseWindows.erase(winID); + } + } + CFRelease(win); + } + + //remove/delete windows not present anymore + map<unsigned int, CutWindow*>::iterator itWin = eraseWindows.begin(); + for(; itWin!=eraseWindows.end(); ++itWin) { + m_windowMap.erase(itWin->first); + delete itWin->second; + } + } + else { + cout<<"Error: No windows found"<<endl; + } + +} + + diff --git a/src/mac/MacWindowsManager.hpp b/src/mac/MacWindowsManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f3d5590ea031bd051222efde8316d6dcbd5d1df8 --- /dev/null +++ b/src/mac/MacWindowsManager.hpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * MacWindowsManager.hpp + * Part of Over + * 2013 Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef MacWindowsManager_h +#define MacWindowsManager_h + +#include <vector> +#include <string> + +#include "../CutWindowsManager.hpp" +#include "MacWindow.hpp" + +class MacWindowsManager : public CutWindowsManager { + public : + MacWindowsManager(); + virtual ~MacWindowsManager(); + virtual void updateWindowsList(); + protected: +}; + +#endif + diff --git a/src/osc/ip/IpEndpointName.cpp b/src/osc/ip/IpEndpointName.cpp new file mode 100755 index 0000000000000000000000000000000000000000..33fdd98733444d7a142b435295150e0b9455e1ca --- /dev/null +++ b/src/osc/ip/IpEndpointName.cpp @@ -0,0 +1,81 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "IpEndpointName.h" + +#include <stdio.h> + +#include "NetworkingUtils.h" + + +unsigned long IpEndpointName::GetHostByName( const char *s ) +{ + return ::GetHostByName(s); +} + + +void IpEndpointName::AddressAsString( char *s ) const +{ + if( address == ANY_ADDRESS ){ + sprintf( s, "<any>" ); + }else{ + sprintf( s, "%d.%d.%d.%d", + (int)((address >> 24) & 0xFF), + (int)((address >> 16) & 0xFF), + (int)((address >> 8) & 0xFF), + (int)(address & 0xFF) ); + } +} + + +void IpEndpointName::AddressAndPortAsString( char *s ) const +{ + if( port == ANY_PORT ){ + if( address == ANY_ADDRESS ){ + sprintf( s, "<any>:<any>" ); + }else{ + sprintf( s, "%d.%d.%d.%d:<any>", + (int)((address >> 24) & 0xFF), + (int)((address >> 16) & 0xFF), + (int)((address >> 8) & 0xFF), + (int)(address & 0xFF) ); + } + }else{ + if( address == ANY_ADDRESS ){ + sprintf( s, "<any>:%d", port ); + }else{ + sprintf( s, "%d.%d.%d.%d:%d", + (int)((address >> 24) & 0xFF), + (int)((address >> 16) & 0xFF), + (int)((address >> 8) & 0xFF), + (int)(address & 0xFF), + (int)port ); + } + } +} diff --git a/src/osc/ip/IpEndpointName.h b/src/osc/ip/IpEndpointName.h new file mode 100755 index 0000000000000000000000000000000000000000..c7b078e711675dbc278f5fdb125aeb7c092268d0 --- /dev/null +++ b/src/osc/ip/IpEndpointName.h @@ -0,0 +1,74 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_IPENDPOINTNAME_H +#define INCLUDED_IPENDPOINTNAME_H + + +class IpEndpointName{ + static unsigned long GetHostByName( const char *s ); +public: + static const unsigned long ANY_ADDRESS = 0xFFFFFFFF; + static const int ANY_PORT = -1; + + IpEndpointName() + : address( ANY_ADDRESS ), port( ANY_PORT ) {} + IpEndpointName( int port_ ) + : address( ANY_ADDRESS ), port( port_ ) {} + IpEndpointName( unsigned long ipAddress_, int port_ ) + : address( ipAddress_ ), port( port_ ) {} + IpEndpointName( const char *addressName, int port_=ANY_PORT ) + : address( GetHostByName( addressName ) ) + , port( port_ ) {} + IpEndpointName( int addressA, int addressB, int addressC, int addressD, int port_=ANY_PORT ) + : address( ( (addressA << 24) | (addressB << 16) | (addressC << 8) | addressD ) ) + , port( port_ ) {} + + // address and port are maintained in host byte order here + unsigned long address; + int port; + + enum { ADDRESS_STRING_LENGTH=17 }; + void AddressAsString( char *s ) const; + + enum { ADDRESS_AND_PORT_STRING_LENGTH=23}; + void AddressAndPortAsString( char *s ) const; +}; + +inline bool operator==( const IpEndpointName& lhs, const IpEndpointName& rhs ) +{ + return (lhs.address == rhs.address && lhs.port == rhs.port ); +} + +inline bool operator!=( const IpEndpointName& lhs, const IpEndpointName& rhs ) +{ + return !(lhs == rhs); +} + +#endif /* INCLUDED_IPENDPOINTNAME_H */ diff --git a/src/osc/ip/NetworkingUtils.h b/src/osc/ip/NetworkingUtils.h new file mode 100755 index 0000000000000000000000000000000000000000..0d6901ca1f617d7eb7de04dae3b2452304b9b588 --- /dev/null +++ b/src/osc/ip/NetworkingUtils.h @@ -0,0 +1,49 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_NETWORKINGUTILS_H +#define INCLUDED_NETWORKINGUTILS_H + + +// in general NetworkInitializer is only used internally, but if you're +// application creates multiple sockets from different threads at runtime you +// should instantiate one of these in main just to make sure the networking +// layer is initialized. +class NetworkInitializer{ +public: + NetworkInitializer(); + ~NetworkInitializer(); +}; + + +// return ip address of host name in host byte order +unsigned long GetHostByName( const char *name ); + + +#endif /* INCLUDED_NETWORKINGUTILS_H */ diff --git a/src/osc/ip/PacketListener.h b/src/osc/ip/PacketListener.h new file mode 100755 index 0000000000000000000000000000000000000000..6647209dc3261bbc9790b99c920ca2dcc67ba665 --- /dev/null +++ b/src/osc/ip/PacketListener.h @@ -0,0 +1,43 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_PACKETLISTENER_H +#define INCLUDED_PACKETLISTENER_H + + +class IpEndpointName; + +class PacketListener{ +public: + virtual ~PacketListener() {} + virtual void ProcessPacket( const char *data, int size, + const IpEndpointName& remoteEndpoint ) = 0; +}; + +#endif /* INCLUDED_PACKETLISTENER_H */ diff --git a/src/osc/ip/TimerListener.h b/src/osc/ip/TimerListener.h new file mode 100755 index 0000000000000000000000000000000000000000..82b11818c1a5707d332e7ba7cbfe479bfe074675 --- /dev/null +++ b/src/osc/ip/TimerListener.h @@ -0,0 +1,40 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_TIMERLISTENER_H +#define INCLUDED_TIMERLISTENER_H + + +class TimerListener{ +public: + virtual ~TimerListener() {} + virtual void TimerExpired() = 0; +}; + +#endif /* INCLUDED_TIMERLISTENER_H */ diff --git a/src/osc/ip/UdpSocket.h b/src/osc/ip/UdpSocket.h new file mode 100755 index 0000000000000000000000000000000000000000..6d9c26d90686a566a6e8b72d986dd7a7176e8a18 --- /dev/null +++ b/src/osc/ip/UdpSocket.h @@ -0,0 +1,158 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_UDPSOCKET_H +#define INCLUDED_UDPSOCKET_H + +#ifndef INCLUDED_NETWORKINGUTILITIES_H +#include "NetworkingUtils.h" +#endif /* INCLUDED_NETWORKINGUTILITIES_H */ + +#ifndef INCLUDED_IPENDPOINTNAME_H +#include "IpEndpointName.h" +#endif /* INCLUDED_IPENDPOINTNAME_H */ + + +class PacketListener; +class TimerListener; + +class UdpSocket; + +class SocketReceiveMultiplexer{ + class Implementation; + Implementation *impl_; + + friend class UdpSocket; + +public: + SocketReceiveMultiplexer(); + ~SocketReceiveMultiplexer(); + + // only call the attach/detach methods _before_ calling Run + + // only one listener per socket, each socket at most once + void AttachSocketListener( UdpSocket *socket, PacketListener *listener ); + void DetachSocketListener( UdpSocket *socket, PacketListener *listener ); + + void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener ); + void AttachPeriodicTimerListener( + int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener ); + void DetachPeriodicTimerListener( TimerListener *listener ); + + void Run(); // loop and block processing messages indefinitely + void RunUntilSigInt(); + void Break(); // call this from a listener to exit once the listener returns + void AsynchronousBreak(); // call this from another thread or signal handler to exit the Run() state +}; + + +class UdpSocket{ + class Implementation; + Implementation *impl_; + + friend class SocketReceiveMultiplexer::Implementation; + +public: + + // ctor throws std::runtime_error if there's a problem + // initializing the socket. + UdpSocket(); + virtual ~UdpSocket(); + + // the socket is created in an unbound, unconnected state + // such a socket can only be used to send to an arbitrary + // address using SendTo(). To use Send() you need to first + // connect to a remote endpoint using Connect(). To use + // ReceiveFrom you need to first bind to a local endpoint + // using Bind(). + + // retrieve the local endpoint name when sending to 'to' + IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const; + + // Connect to a remote endpoint which is used as the target + // for calls to Send() + void Connect( const IpEndpointName& remoteEndpoint ); + void Send( const char *data, int size ); + void SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size ); + + + // Bind a local endpoint to receive incoming data. Endpoint + // can be 'any' for the system to choose an endpoint + void Bind( const IpEndpointName& localEndpoint ); + bool IsBound() const; + + int ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size ); +}; + + +// convenience classes for transmitting and receiving +// they just call Connect and/or Bind in the ctor. +// note that you can still use a receive socket +// for transmitting etc + +class UdpTransmitSocket : public UdpSocket{ +public: + UdpTransmitSocket( const IpEndpointName& remoteEndpoint ) + { Connect( remoteEndpoint ); } +}; + + +class UdpReceiveSocket : public UdpSocket{ +public: + UdpReceiveSocket( const IpEndpointName& localEndpoint ) + { Bind( localEndpoint ); } +}; + + +// UdpListeningReceiveSocket provides a simple way to bind one listener +// to a single socket without having to manually set up a SocketReceiveMultiplexer + +class UdpListeningReceiveSocket : public UdpSocket{ + SocketReceiveMultiplexer mux_; + PacketListener *listener_; +public: + UdpListeningReceiveSocket( const IpEndpointName& localEndpoint, PacketListener *listener ) + : listener_( listener ) + { + Bind( localEndpoint ); + mux_.AttachSocketListener( this, listener_ ); + } + + ~UdpListeningReceiveSocket() + { mux_.DetachSocketListener( this, listener_ ); } + + // see SocketReceiveMultiplexer above for the behaviour of these methods... + void Run() { mux_.Run(); } + void RunUntilSigInt() { mux_.RunUntilSigInt(); } + void Break() { mux_.Break(); } + void AsynchronousBreak() { mux_.AsynchronousBreak(); } +}; + + +#endif /* INCLUDED_UDPSOCKET_H */ diff --git a/src/osc/ip/posix/NetworkingUtils.cpp b/src/osc/ip/posix/NetworkingUtils.cpp new file mode 100755 index 0000000000000000000000000000000000000000..d3e595c43e16edb55f18d1613c11d441f416128b --- /dev/null +++ b/src/osc/ip/posix/NetworkingUtils.cpp @@ -0,0 +1,57 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "../NetworkingUtils.h" + +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <stdio.h> + + + +NetworkInitializer::NetworkInitializer() {} + +NetworkInitializer::~NetworkInitializer() {} + + +unsigned long GetHostByName( const char *name ) +{ + unsigned long result = 0; + + struct hostent *h = gethostbyname( name ); + if( h ){ + struct in_addr a; + memcpy( &a, h->h_addr_list[0], h->h_length ); + result = ntohl(a.s_addr); + } + + return result; +} diff --git a/src/osc/ip/posix/UdpSocket.cpp b/src/osc/ip/posix/UdpSocket.cpp new file mode 100755 index 0000000000000000000000000000000000000000..01565ca3ee606986be219c0ff6fd72d6c2df647f --- /dev/null +++ b/src/osc/ip/posix/UdpSocket.cpp @@ -0,0 +1,546 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "..//UdpSocket.h" + +#include <vector> +#include <algorithm> +#include <stdexcept> +#include <assert.h> +#include <signal.h> +#include <math.h> +#include <errno.h> +#include <string.h> // for memset + +#include <pthread.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <netdb.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> // for sockaddr_in + +#include "../PacketListener.h" +#include "../TimerListener.h" + + +#if defined(__APPLE__) && !defined(_SOCKLEN_T) +// pre system 10.3 didn have socklen_t +typedef ssize_t socklen_t; +#endif + + +static void SockaddrFromIpEndpointName( struct sockaddr_in& sockAddr, const IpEndpointName& endpoint ) +{ + memset( (char *)&sockAddr, 0, sizeof(sockAddr ) ); + sockAddr.sin_family = AF_INET; + + sockAddr.sin_addr.s_addr = + (endpoint.address == IpEndpointName::ANY_ADDRESS) + ? INADDR_ANY + : htonl( endpoint.address ); + + sockAddr.sin_port = + (endpoint.port == IpEndpointName::ANY_PORT) + ? 0 + : htons( endpoint.port ); +} + + +static IpEndpointName IpEndpointNameFromSockaddr( const struct sockaddr_in& sockAddr ) +{ + return IpEndpointName( + (sockAddr.sin_addr.s_addr == INADDR_ANY) + ? IpEndpointName::ANY_ADDRESS + : ntohl( sockAddr.sin_addr.s_addr ), + (sockAddr.sin_port == 0) + ? IpEndpointName::ANY_PORT + : ntohs( sockAddr.sin_port ) + ); +} + + +class UdpSocket::Implementation{ + bool isBound_; + bool isConnected_; + + int socket_; + struct sockaddr_in connectedAddr_; + struct sockaddr_in sendToAddr_; + +public: + + Implementation() + : isBound_( false ) + , isConnected_( false ) + , socket_( -1 ) + { + if( (socket_ = socket( AF_INET, SOCK_DGRAM, 0 )) == -1 ){ + throw std::runtime_error("unable to create udp socket\n"); + } + + memset( &sendToAddr_, 0, sizeof(sendToAddr_) ); + sendToAddr_.sin_family = AF_INET; + } + + ~Implementation() + { + if (socket_ != -1) close(socket_); + } + + IpEndpointName LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const + { + assert( isBound_ ); + + // first connect the socket to the remote server + + struct sockaddr_in connectSockAddr; + SockaddrFromIpEndpointName( connectSockAddr, remoteEndpoint ); + + if (connect(socket_, (struct sockaddr *)&connectSockAddr, sizeof(connectSockAddr)) < 0) { + throw std::runtime_error("unable to connect udp socket\n"); + } + + // get the address + + struct sockaddr_in sockAddr; + memset( (char *)&sockAddr, 0, sizeof(sockAddr ) ); + socklen_t length = sizeof(sockAddr); + if (getsockname(socket_, (struct sockaddr *)&sockAddr, &length) < 0) { + throw std::runtime_error("unable to getsockname\n"); + } + + if( isConnected_ ){ + // reconnect to the connected address + + if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) { + throw std::runtime_error("unable to connect udp socket\n"); + } + + }else{ + // unconnect from the remote address + + struct sockaddr_in unconnectSockAddr; + memset( (char *)&unconnectSockAddr, 0, sizeof(unconnectSockAddr ) ); + unconnectSockAddr.sin_family = AF_UNSPEC; + // address fields are zero + int connectResult = connect(socket_, (struct sockaddr *)&unconnectSockAddr, sizeof(unconnectSockAddr)); + if ( connectResult < 0 && errno != EAFNOSUPPORT ) { + throw std::runtime_error("unable to un-connect udp socket\n"); + } + } + + return IpEndpointNameFromSockaddr( sockAddr ); + } + + void Connect( const IpEndpointName& remoteEndpoint ) + { + SockaddrFromIpEndpointName( connectedAddr_, remoteEndpoint ); + + if (connect(socket_, (struct sockaddr *)&connectedAddr_, sizeof(connectedAddr_)) < 0) { + throw std::runtime_error("unable to connect udp socket\n"); + } + + isConnected_ = true; + } + + void Send( const char *data, int size ) + { + assert( isConnected_ ); + + send( socket_, data, size, 0 ); + } + + void SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size ) + { + sendToAddr_.sin_addr.s_addr = htonl( remoteEndpoint.address ); + sendToAddr_.sin_port = htons( remoteEndpoint.port ); + + sendto( socket_, data, size, 0, (sockaddr*)&sendToAddr_, sizeof(sendToAddr_) ); + } + + void Bind( const IpEndpointName& localEndpoint ) + { + struct sockaddr_in bindSockAddr; + SockaddrFromIpEndpointName( bindSockAddr, localEndpoint ); + + if (bind(socket_, (struct sockaddr *)&bindSockAddr, sizeof(bindSockAddr)) < 0) { + throw std::runtime_error("unable to bind udp socket\n"); + } + + isBound_ = true; + } + + bool IsBound() const { return isBound_; } + + int ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size ) + { + assert( isBound_ ); + + struct sockaddr_in fromAddr; + socklen_t fromAddrLen = sizeof(fromAddr); + + int result = recvfrom(socket_, data, size, 0, + (struct sockaddr *) &fromAddr, (socklen_t*)&fromAddrLen); + if( result < 0 ) + return 0; + + remoteEndpoint.address = ntohl(fromAddr.sin_addr.s_addr); + remoteEndpoint.port = ntohs(fromAddr.sin_port); + + return result; + } + + int Socket() { return socket_; } +}; + +UdpSocket::UdpSocket() +{ + impl_ = new Implementation(); +} + +UdpSocket::~UdpSocket() +{ + delete impl_; +} + +IpEndpointName UdpSocket::LocalEndpointFor( const IpEndpointName& remoteEndpoint ) const +{ + return impl_->LocalEndpointFor( remoteEndpoint ); +} + +void UdpSocket::Connect( const IpEndpointName& remoteEndpoint ) +{ + impl_->Connect( remoteEndpoint ); +} + +void UdpSocket::Send( const char *data, int size ) +{ + impl_->Send( data, size ); +} + +void UdpSocket::SendTo( const IpEndpointName& remoteEndpoint, const char *data, int size ) +{ + impl_->SendTo( remoteEndpoint, data, size ); +} + +void UdpSocket::Bind( const IpEndpointName& localEndpoint ) +{ + impl_->Bind( localEndpoint ); +} + +bool UdpSocket::IsBound() const +{ + return impl_->IsBound(); +} + +int UdpSocket::ReceiveFrom( IpEndpointName& remoteEndpoint, char *data, int size ) +{ + return impl_->ReceiveFrom( remoteEndpoint, data, size ); +} + + +struct AttachedTimerListener{ + AttachedTimerListener( int id, int p, TimerListener *tl ) + : initialDelayMs( id ) + , periodMs( p ) + , listener( tl ) {} + int initialDelayMs; + int periodMs; + TimerListener *listener; +}; + + +static bool CompareScheduledTimerCalls( + const std::pair< double, AttachedTimerListener > & lhs, const std::pair< double, AttachedTimerListener > & rhs ) +{ + return lhs.first < rhs.first; +} + + +SocketReceiveMultiplexer *multiplexerInstanceToAbortWithSigInt_ = 0; + +extern "C" /*static*/ void InterruptSignalHandler( int ); +/*static*/ void InterruptSignalHandler( int ) +{ + multiplexerInstanceToAbortWithSigInt_->AsynchronousBreak(); + signal( SIGINT, SIG_DFL ); +} + + +class SocketReceiveMultiplexer::Implementation{ + std::vector< std::pair< PacketListener*, UdpSocket* > > socketListeners_; + std::vector< AttachedTimerListener > timerListeners_; + + volatile bool break_; + int breakPipe_[2]; // [0] is the reader descriptor and [1] the writer + + double GetCurrentTimeMs() const + { + struct timeval t; + + gettimeofday( &t, 0 ); + + return ((double)t.tv_sec*1000.) + ((double)t.tv_usec / 1000.); + } + +public: + Implementation() + { + if( pipe(breakPipe_) != 0 ) + throw std::runtime_error( "creation of asynchronous break pipes failed\n" ); + } + + ~Implementation() + { + close( breakPipe_[0] ); + close( breakPipe_[1] ); + } + + void AttachSocketListener( UdpSocket *socket, PacketListener *listener ) + { + assert( std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) ) == socketListeners_.end() ); + // we don't check that the same socket has been added multiple times, even though this is an error + socketListeners_.push_back( std::make_pair( listener, socket ) ); + } + + void DetachSocketListener( UdpSocket *socket, PacketListener *listener ) + { + std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = + std::find( socketListeners_.begin(), socketListeners_.end(), std::make_pair(listener, socket) ); + assert( i != socketListeners_.end() ); + + socketListeners_.erase( i ); + } + + void AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener ) + { + timerListeners_.push_back( AttachedTimerListener( periodMilliseconds, periodMilliseconds, listener ) ); + } + + void AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener ) + { + timerListeners_.push_back( AttachedTimerListener( initialDelayMilliseconds, periodMilliseconds, listener ) ); + } + + void DetachPeriodicTimerListener( TimerListener *listener ) + { + std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin(); + while( i != timerListeners_.end() ){ + if( i->listener == listener ) + break; + ++i; + } + + assert( i != timerListeners_.end() ); + + timerListeners_.erase( i ); + } + + void Run() + { + break_ = false; + + // configure the master fd_set for select() + + fd_set masterfds, tempfds; + FD_ZERO( &masterfds ); + FD_ZERO( &tempfds ); + + // in addition to listening to the inbound sockets we + // also listen to the asynchronous break pipe, so that AsynchronousBreak() + // can break us out of select() from another thread. + FD_SET( breakPipe_[0], &masterfds ); + int fdmax = breakPipe_[0]; + + for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin(); + i != socketListeners_.end(); ++i ){ + + if( fdmax < i->second->impl_->Socket() ) + fdmax = i->second->impl_->Socket(); + FD_SET( i->second->impl_->Socket(), &masterfds ); + } + + + // configure the timer queue + double currentTimeMs = GetCurrentTimeMs(); + + // expiry time ms, listener + std::vector< std::pair< double, AttachedTimerListener > > timerQueue_; + for( std::vector< AttachedTimerListener >::iterator i = timerListeners_.begin(); + i != timerListeners_.end(); ++i ) + timerQueue_.push_back( std::make_pair( currentTimeMs + i->initialDelayMs, *i ) ); + std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls ); + + const int MAX_BUFFER_SIZE = 4098; + char *data = new char[ MAX_BUFFER_SIZE ]; + IpEndpointName remoteEndpoint; + + struct timeval timeout; + + while( !break_ ){ + tempfds = masterfds; + + struct timeval *timeoutPtr = 0; + if( !timerQueue_.empty() ){ + double timeoutMs = timerQueue_.front().first - GetCurrentTimeMs(); + if( timeoutMs < 0 ) + timeoutMs = 0; + + // 1000000 microseconds in a second + timeout.tv_sec = (long)(timeoutMs * .001); + timeout.tv_usec = (long)((timeoutMs - (timeout.tv_sec * 1000)) * 1000); + timeoutPtr = &timeout; + } + + if( select( fdmax + 1, &tempfds, 0, 0, timeoutPtr ) < 0 && errno != EINTR ){ + throw std::runtime_error("select failed\n"); + } + + if ( FD_ISSET( breakPipe_[0], &tempfds ) ){ + // clear pending data from the asynchronous break pipe + char c; + read( breakPipe_[0], &c, 1 ); + } + + if( break_ ) + break; + + for( std::vector< std::pair< PacketListener*, UdpSocket* > >::iterator i = socketListeners_.begin(); + i != socketListeners_.end(); ++i ){ + + if( FD_ISSET( i->second->impl_->Socket(), &tempfds ) ){ + + int size = i->second->ReceiveFrom( remoteEndpoint, data, MAX_BUFFER_SIZE ); + if( size > 0 ){ + i->first->ProcessPacket( data, size, remoteEndpoint ); + if( break_ ) + break; + } + } + } + + // execute any expired timers + currentTimeMs = GetCurrentTimeMs(); + bool resort = false; + for( std::vector< std::pair< double, AttachedTimerListener > >::iterator i = timerQueue_.begin(); + i != timerQueue_.end() && i->first <= currentTimeMs; ++i ){ + + i->second.listener->TimerExpired(); + if( break_ ) + break; + + i->first += i->second.periodMs; + resort = true; + } + if( resort ) + std::sort( timerQueue_.begin(), timerQueue_.end(), CompareScheduledTimerCalls ); + } + + delete [] data; + } + + void Break() + { + break_ = true; + } + + void AsynchronousBreak() + { + break_ = true; + + // Send a termination message to the asynchronous break pipe, so select() will return + write( breakPipe_[1], "!", 1 ); + } +}; + + + +SocketReceiveMultiplexer::SocketReceiveMultiplexer() +{ + impl_ = new Implementation(); +} + +SocketReceiveMultiplexer::~SocketReceiveMultiplexer() +{ + delete impl_; +} + +void SocketReceiveMultiplexer::AttachSocketListener( UdpSocket *socket, PacketListener *listener ) +{ + impl_->AttachSocketListener( socket, listener ); +} + +void SocketReceiveMultiplexer::DetachSocketListener( UdpSocket *socket, PacketListener *listener ) +{ + impl_->DetachSocketListener( socket, listener ); +} + +void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int periodMilliseconds, TimerListener *listener ) +{ + impl_->AttachPeriodicTimerListener( periodMilliseconds, listener ); +} + +void SocketReceiveMultiplexer::AttachPeriodicTimerListener( int initialDelayMilliseconds, int periodMilliseconds, TimerListener *listener ) +{ + impl_->AttachPeriodicTimerListener( initialDelayMilliseconds, periodMilliseconds, listener ); +} + +void SocketReceiveMultiplexer::DetachPeriodicTimerListener( TimerListener *listener ) +{ + impl_->DetachPeriodicTimerListener( listener ); +} + +void SocketReceiveMultiplexer::Run() +{ + impl_->Run(); +} + +void SocketReceiveMultiplexer::RunUntilSigInt() +{ + assert( multiplexerInstanceToAbortWithSigInt_ == 0 ); /* at present we support only one multiplexer instance running until sig int */ + multiplexerInstanceToAbortWithSigInt_ = this; + signal( SIGINT, InterruptSignalHandler ); + impl_->Run(); + signal( SIGINT, SIG_DFL ); + multiplexerInstanceToAbortWithSigInt_ = 0; +} + +void SocketReceiveMultiplexer::Break() +{ + impl_->Break(); +} + +void SocketReceiveMultiplexer::AsynchronousBreak() +{ + impl_->AsynchronousBreak(); +} + diff --git a/src/osc/osc/MessageMappingOscPacketListener.h b/src/osc/osc/MessageMappingOscPacketListener.h new file mode 100755 index 0000000000000000000000000000000000000000..017bf05b170382abd79de02583be6e4177b95143 --- /dev/null +++ b/src/osc/osc/MessageMappingOscPacketListener.h @@ -0,0 +1,73 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H +#define INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H + +#include <string.h> +#include <map> + +#include "OscPacketListener.h" + + + +namespace osc{ + +template< class T > +class MessageMappingOscPacketListener : public OscPacketListener{ +public: + typedef void (T::*function_type)(const osc::ReceivedMessage&, const IpEndpointName&); + +protected: + void RegisterMessageFunction( const char *addressPattern, function_type f ) + { + functions_.insert( std::make_pair( addressPattern, f ) ); + } + + virtual void ProcessMessage( const osc::ReceivedMessage& m, + const IpEndpointName& remoteEndpoint ) + { + typename function_map_type::iterator i = functions_.find( m.AddressPattern() ); + if( i != functions_.end() ) + (dynamic_cast<T*>(this)->*(i->second))( m, remoteEndpoint ); + } + +private: + struct cstr_compare{ + bool operator()( const char *lhs, const char *rhs ) const + { return strcmp( lhs, rhs ) < 0; } + }; + + typedef std::map<const char*, function_type, cstr_compare> function_map_type; + function_map_type functions_; +}; + +} // namespace osc + +#endif /* INCLUDED_MESSAGEMAPPINGOSCPACKETLISTENER_H */ \ No newline at end of file diff --git a/src/osc/osc/OscException.h b/src/osc/osc/OscException.h new file mode 100755 index 0000000000000000000000000000000000000000..cd8d567a091311ccf22f74cf0eed4efd37f9fa9b --- /dev/null +++ b/src/osc/osc/OscException.h @@ -0,0 +1,54 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSC_EXCEPTION_H +#define INCLUDED_OSC_EXCEPTION_H + +#include <exception> + +namespace osc{ + +class Exception : public std::exception { + const char *what_; + +public: + Exception() throw() {} + Exception( const Exception& src ) throw() + : what_( src.what_ ) {} + Exception( const char *w ) throw() + : what_( w ) {} + Exception& operator=( const Exception& src ) throw() + { what_ = src.what_; return *this; } + virtual ~Exception() throw() {} + virtual const char* what() const throw() { return what_; } +}; + +} // namespace osc + +#endif /* INCLUDED_OSC_EXCEPTION_H */ diff --git a/src/osc/osc/OscHostEndianness.h b/src/osc/osc/OscHostEndianness.h new file mode 100755 index 0000000000000000000000000000000000000000..b542ef5dd2563bdbbdfd19b6b278b8defc3d7fa4 --- /dev/null +++ b/src/osc/osc/OscHostEndianness.h @@ -0,0 +1,70 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef OSC_HOSTENDIANNESS_H +#define OSC_HOSTENDIANNESS_H + +/* + Make sure either OSC_HOST_LITTLE_ENDIAN or OSC_HOST_BIG_ENDIAN is defined + + If you know a way to enhance the detection below for Linux and/or MacOSX + please let me know! I've tried a few things which don't work. +*/ +#define OSC_HOST_LITTLE_ENDIAN 1 + +#if defined(OSC_HOST_LITTLE_ENDIAN) || defined(OSC_HOST_BIG_ENDIAN) + +// you can define one of the above symbols from the command line +// then you don't have to edit this file. + +#elif defined(__WIN32__) || defined(WIN32) + +// assume that __WIN32__ is only defined on little endian systems + +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN + +#elif defined(__APPLE__) + +#if defined(__LITTLE_ENDIAN__) +#define OSC_HOST_LITTLE_ENDIAN 1 +#undef OSC_HOST_BIG_ENDIAN +#else +#define OSC_HOST_BIG_ENDIAN 1 +#undef OSC_HOST_LITTLE_ENDIAN +#endif + +#else + +#error please edit OSCHostEndianness.h to configure endianness + +#endif + +#endif /* OSC_HOSTENDIANNESS_H */ + diff --git a/src/osc/osc/OscOutboundPacketStream.cpp b/src/osc/osc/OscOutboundPacketStream.cpp new file mode 100755 index 0000000000000000000000000000000000000000..75b18006bc6468bb8f400a59a4984a0512d2e434 --- /dev/null +++ b/src/osc/osc/OscOutboundPacketStream.cpp @@ -0,0 +1,639 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "OscOutboundPacketStream.h" + +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#if defined(__WIN32__) || defined(WIN32) +#include <malloc.h> // for alloca +#endif + +#include "OscHostEndianness.h" + + +namespace osc{ + +static void FromInt32( char *p, int32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast<int32*>(p) = x; +#endif +} + + +static void FromUInt32( char *p, uint32 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.i = x; + + p[3] = u.c[0]; + p[2] = u.c[1]; + p[1] = u.c[2]; + p[0] = u.c[3]; +#else + *reinterpret_cast<uint32*>(p) = x; +#endif +} + + +static void FromInt64( char *p, int64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast<int64*>(p) = x; +#endif +} + + +static void FromUInt64( char *p, uint64 x ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[8]; + } u; + + u.i = x; + + p[7] = u.c[0]; + p[6] = u.c[1]; + p[5] = u.c[2]; + p[4] = u.c[3]; + p[3] = u.c[4]; + p[2] = u.c[5]; + p[1] = u.c[6]; + p[0] = u.c[7]; +#else + *reinterpret_cast<uint64*>(p) = x; +#endif +} + + +static inline long RoundUp4( long x ) +{ + return ((x-1) & (~0x03L)) + 4; +} + + +OutboundPacketStream::OutboundPacketStream( char *buffer, unsigned long capacity ) + : data_( buffer ) + , end_( data_ + capacity ) + , typeTagsCurrent_( end_ ) + , messageCursor_( data_ ) + , argumentCurrent_( data_ ) + , elementSizePtr_( 0 ) + , messageIsInProgress_( false ) +{ + +} + + +OutboundPacketStream::~OutboundPacketStream() +{ + +} + + +char *OutboundPacketStream::BeginElement( char *beginPtr ) +{ + if( elementSizePtr_ == 0 ){ + + elementSizePtr_ = reinterpret_cast<uint32*>(data_); + + return beginPtr; + + }else{ + // store an offset to the old element size ptr in the element size slot + // we store an offset rather than the actual pointer to be 64 bit clean. + *reinterpret_cast<uint32*>(beginPtr) = + (uint32)(reinterpret_cast<char*>(elementSizePtr_) - data_); + + elementSizePtr_ = reinterpret_cast<uint32*>(beginPtr); + + return beginPtr + 4; + } +} + + +void OutboundPacketStream::EndElement( char *endPtr ) +{ + assert( elementSizePtr_ != 0 ); + + if( elementSizePtr_ == reinterpret_cast<uint32*>(data_) ){ + + elementSizePtr_ = 0; + + }else{ + // while building an element, an offset to the containing element's + // size slot is stored in the elements size slot (or a ptr to data_ + // if there is no containing element). We retrieve that here + uint32 *previousElementSizePtr = + (uint32*)(data_ + *reinterpret_cast<uint32*>(elementSizePtr_)); + + // then we store the element size in the slot, note that the element + // size does not include the size slot, hence the - 4 below. + uint32 elementSize = + (endPtr - reinterpret_cast<char*>(elementSizePtr_)) - 4; + FromUInt32( reinterpret_cast<char*>(elementSizePtr_), elementSize ); + + // finally, we reset the element size ptr to the containing element + elementSizePtr_ = previousElementSizePtr; + } +} + + +bool OutboundPacketStream::ElementSizeSlotRequired() const +{ + return (elementSizePtr_ != 0); +} + + +void OutboundPacketStream::CheckForAvailableBundleSpace() +{ + unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + 16; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableMessageSpace( const char *addressPattern ) +{ + // plus 4 for at least four bytes of type tag + unsigned long required = Size() + ((ElementSizeSlotRequired())?4:0) + + RoundUp4(strlen(addressPattern) + 1) + 4; + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::CheckForAvailableArgumentSpace( long argumentLength ) +{ + // plus three for extra type tag, comma and null terminator + unsigned long required = (argumentCurrent_ - data_) + argumentLength + + RoundUp4( (end_ - typeTagsCurrent_) + 3 ); + + if( required > Capacity() ) + throw OutOfBufferMemoryException(); +} + + +void OutboundPacketStream::Clear() +{ + typeTagsCurrent_ = end_; + messageCursor_ = data_; + argumentCurrent_ = data_; + elementSizePtr_ = 0; + messageIsInProgress_ = false; +} + + +unsigned int OutboundPacketStream::Capacity() const +{ + return end_ - data_; +} + + +unsigned int OutboundPacketStream::Size() const +{ + unsigned int result = argumentCurrent_ - data_; + if( IsMessageInProgress() ){ + // account for the length of the type tag string. the total type tag + // includes an initial comma, plus at least one terminating \0 + result += RoundUp4( (end_ - typeTagsCurrent_) + 2 ); + } + + return result; +} + + +const char *OutboundPacketStream::Data() const +{ + return data_; +} + + +bool OutboundPacketStream::IsReady() const +{ + return (!IsMessageInProgress() && !IsBundleInProgress()); +} + + +bool OutboundPacketStream::IsMessageInProgress() const +{ + return messageIsInProgress_; +} + + +bool OutboundPacketStream::IsBundleInProgress() const +{ + return (elementSizePtr_ != 0); +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleInitiator& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableBundleSpace(); + + messageCursor_ = BeginElement( messageCursor_ ); + + memcpy( messageCursor_, "#bundle\0", 8 ); + FromUInt64( messageCursor_ + 8, rhs.timeTag ); + + messageCursor_ += 16; + argumentCurrent_ = messageCursor_; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BundleTerminator& rhs ) +{ + (void) rhs; + + if( !IsBundleInProgress() ) + throw BundleNotInProgressException(); + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + EndElement( messageCursor_ ); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const BeginMessage& rhs ) +{ + if( IsMessageInProgress() ) + throw MessageInProgressException(); + + CheckForAvailableMessageSpace( rhs.addressPattern ); + + messageCursor_ = BeginElement( messageCursor_ ); + + strcpy( messageCursor_, rhs.addressPattern ); + unsigned long rhsLength = strlen(rhs.addressPattern); + messageCursor_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *messageCursor_++ = '\0'; + ++i; + } + + argumentCurrent_ = messageCursor_; + typeTagsCurrent_ = end_; + + messageIsInProgress_ = true; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MessageTerminator& rhs ) +{ + (void) rhs; + + if( !IsMessageInProgress() ) + throw MessageNotInProgressException(); + + int typeTagsCount = end_ - typeTagsCurrent_; + + if( typeTagsCount ){ + + char *tempTypeTags = (char*)alloca(typeTagsCount); + memcpy( tempTypeTags, typeTagsCurrent_, typeTagsCount ); + + // slot size includes comma and null terminator + int typeTagSlotSize = RoundUp4( typeTagsCount + 2 ); + + uint32 argumentsSize = argumentCurrent_ - messageCursor_; + + memmove( messageCursor_ + typeTagSlotSize, messageCursor_, argumentsSize ); + + messageCursor_[0] = ','; + // copy type tags in reverse (really forward) order + for( int i=0; i < typeTagsCount; ++i ) + messageCursor_[i+1] = tempTypeTags[ (typeTagsCount-1) - i ]; + + char *p = messageCursor_ + 1 + typeTagsCount; + for( int i=0; i < (typeTagSlotSize - (typeTagsCount + 1)); ++i ) + *p++ = '\0'; + + typeTagsCurrent_ = end_; + + // advance messageCursor_ for next message + messageCursor_ += typeTagSlotSize + argumentsSize; + + }else{ + // send an empty type tags string + memcpy( messageCursor_, ",\0\0\0", 4 ); + + // advance messageCursor_ for next message + messageCursor_ += 4; + } + + argumentCurrent_ = messageCursor_; + + EndElement( messageCursor_ ); + + messageIsInProgress_ = false; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( bool rhs ) +{ + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = (char)((rhs) ? TRUE_TYPE_TAG : FALSE_TYPE_TAG); + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const NilType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = NIL_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const InfinitumType& rhs ) +{ + (void) rhs; + CheckForAvailableArgumentSpace(0); + + *(--typeTagsCurrent_) = INFINITUM_TYPE_TAG; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int32 rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = INT32_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( float rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = FLOAT_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.f = rhs; + + argumentCurrent_[3] = u.c[0]; + argumentCurrent_[2] = u.c[1]; + argumentCurrent_[1] = u.c[2]; + argumentCurrent_[0] = u.c[3]; +#else + *reinterpret_cast<float*>(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( char rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = CHAR_TYPE_TAG; + FromInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const RgbaColor& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = RGBA_COLOR_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const MidiMessage& rhs ) +{ + CheckForAvailableArgumentSpace(4); + + *(--typeTagsCurrent_) = MIDI_MESSAGE_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs ); + argumentCurrent_ += 4; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( int64 rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = INT64_TYPE_TAG; + FromInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const TimeTag& rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = TIME_TAG_TYPE_TAG; + FromUInt64( argumentCurrent_, rhs ); + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( double rhs ) +{ + CheckForAvailableArgumentSpace(8); + + *(--typeTagsCurrent_) = DOUBLE_TYPE_TAG; + +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double f; + char c[8]; + } u; + + u.f = rhs; + + argumentCurrent_[7] = u.c[0]; + argumentCurrent_[6] = u.c[1]; + argumentCurrent_[5] = u.c[2]; + argumentCurrent_[4] = u.c[3]; + argumentCurrent_[3] = u.c[4]; + argumentCurrent_[2] = u.c[5]; + argumentCurrent_[1] = u.c[6]; + argumentCurrent_[0] = u.c[7]; +#else + *reinterpret_cast<double*>(argumentCurrent_) = rhs; +#endif + + argumentCurrent_ += 8; + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const char *rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(strlen(rhs) + 1) ); + + *(--typeTagsCurrent_) = STRING_TYPE_TAG; + strcpy( argumentCurrent_, rhs ); + unsigned long rhsLength = strlen(rhs); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Symbol& rhs ) +{ + CheckForAvailableArgumentSpace( RoundUp4(strlen(rhs) + 1) ); + + *(--typeTagsCurrent_) = SYMBOL_TYPE_TAG; + strcpy( argumentCurrent_, rhs ); + unsigned long rhsLength = strlen(rhs); + argumentCurrent_ += rhsLength + 1; + + // zero pad to 4-byte boundary + unsigned long i = rhsLength + 1; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + + +OutboundPacketStream& OutboundPacketStream::operator<<( const Blob& rhs ) +{ + CheckForAvailableArgumentSpace( 4 + RoundUp4(rhs.size) ); + + *(--typeTagsCurrent_) = BLOB_TYPE_TAG; + FromUInt32( argumentCurrent_, rhs.size ); + argumentCurrent_ += 4; + + memcpy( argumentCurrent_, rhs.data, rhs.size ); + argumentCurrent_ += rhs.size; + + // zero pad to 4-byte boundary + unsigned long i = rhs.size; + while( i & 0x3 ){ + *argumentCurrent_++ = '\0'; + ++i; + } + + return *this; +} + +} // namespace osc + + diff --git a/src/osc/osc/OscOutboundPacketStream.h b/src/osc/osc/OscOutboundPacketStream.h new file mode 100755 index 0000000000000000000000000000000000000000..317e4b28eef2d4277f9addad0a2036bf5afa9919 --- /dev/null +++ b/src/osc/osc/OscOutboundPacketStream.h @@ -0,0 +1,142 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCOUTBOUNDPACKET_H +#define INCLUDED_OSCOUTBOUNDPACKET_H + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + +class OutOfBufferMemoryException : public Exception{ +public: + OutOfBufferMemoryException( const char *w="out of buffer memory" ) + : Exception( w ) {} +}; + +class BundleNotInProgressException : public Exception{ +public: + BundleNotInProgressException( + const char *w="call to EndBundle when bundle is not in progress" ) + : Exception( w ) {} +}; + +class MessageInProgressException : public Exception{ +public: + MessageInProgressException( + const char *w="opening or closing bundle or message while message is in progress" ) + : Exception( w ) {} +}; + +class MessageNotInProgressException : public Exception{ +public: + MessageNotInProgressException( + const char *w="call to EndMessage when message is not in progress" ) + : Exception( w ) {} +}; + + +class OutboundPacketStream{ +public: + OutboundPacketStream( char *buffer, unsigned long capacity ); + ~OutboundPacketStream(); + + void Clear(); + + unsigned int Capacity() const; + + // invariant: size() is valid even while building a message. + unsigned int Size() const; + + const char *Data() const; + + // indicates that all messages have been closed with a matching EndMessage + // and all bundles have been closed with a matching EndBundle + bool IsReady() const; + + bool IsMessageInProgress() const; + bool IsBundleInProgress() const; + + OutboundPacketStream& operator<<( const BundleInitiator& rhs ); + OutboundPacketStream& operator<<( const BundleTerminator& rhs ); + + OutboundPacketStream& operator<<( const BeginMessage& rhs ); + OutboundPacketStream& operator<<( const MessageTerminator& rhs ); + + OutboundPacketStream& operator<<( bool rhs ); + OutboundPacketStream& operator<<( const NilType& rhs ); + OutboundPacketStream& operator<<( const InfinitumType& rhs ); + OutboundPacketStream& operator<<( int32 rhs ); + +#ifndef x86_64 + OutboundPacketStream& operator<<( int rhs ) + { *this << (int32)rhs; return *this; } +#endif + + OutboundPacketStream& operator<<( float rhs ); + OutboundPacketStream& operator<<( char rhs ); + OutboundPacketStream& operator<<( const RgbaColor& rhs ); + OutboundPacketStream& operator<<( const MidiMessage& rhs ); + OutboundPacketStream& operator<<( int64 rhs ); + OutboundPacketStream& operator<<( const TimeTag& rhs ); + OutboundPacketStream& operator<<( double rhs ); + OutboundPacketStream& operator<<( const char* rhs ); + OutboundPacketStream& operator<<( const Symbol& rhs ); + OutboundPacketStream& operator<<( const Blob& rhs ); + +private: + + char *BeginElement( char *beginPtr ); + void EndElement( char *endPtr ); + + bool ElementSizeSlotRequired() const; + void CheckForAvailableBundleSpace(); + void CheckForAvailableMessageSpace( const char *addressPattern ); + void CheckForAvailableArgumentSpace( long argumentLength ); + + char *data_; + char *end_; + + char *typeTagsCurrent_; // stored in reverse order + char *messageCursor_; + char *argumentCurrent_; + + // elementSizePtr_ has two special values: 0 indicates that a bundle + // isn't open, and elementSizePtr_==data_ indicates that a bundle is + // open but that it doesn't have a size slot (ie the outermost bundle) + uint32 *elementSizePtr_; + + bool messageIsInProgress_; +}; + +} // namespace osc + +#endif /* INCLUDED_OSC_OUTBOUND_PACKET_H */ diff --git a/src/osc/osc/OscPacketListener.h b/src/osc/osc/OscPacketListener.h new file mode 100755 index 0000000000000000000000000000000000000000..bc322b7bd61abef955a33d67fb1a144cd40ddf59 --- /dev/null +++ b/src/osc/osc/OscPacketListener.h @@ -0,0 +1,72 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCPACKETLISTENER_H +#define INCLUDED_OSCPACKETLISTENER_H + +#include "OscReceivedElements.h" +#include "../ip/PacketListener.h" + + +namespace osc{ + +class OscPacketListener : public PacketListener{ +protected: + virtual void ProcessBundle( const osc::ReceivedBundle& b, + const IpEndpointName& remoteEndpoint ) + { + // ignore bundle time tag for now + + for( ReceivedBundle::const_iterator i = b.ElementsBegin(); + i != b.ElementsEnd(); ++i ){ + if( i->IsBundle() ) + ProcessBundle( ReceivedBundle(*i), remoteEndpoint ); + else + ProcessMessage( ReceivedMessage(*i), remoteEndpoint ); + } + } + + virtual void ProcessMessage( const osc::ReceivedMessage& m, + const IpEndpointName& remoteEndpoint ) = 0; + +public: + virtual void ProcessPacket( const char *data, int size, + const IpEndpointName& remoteEndpoint ) + { + osc::ReceivedPacket p( data, size ); + if( p.IsBundle() ) + ProcessBundle( ReceivedBundle(p), remoteEndpoint ); + else + ProcessMessage( ReceivedMessage(p), remoteEndpoint ); + } +}; + +} // namespace osc + +#endif /* INCLUDED_OSCPACKETLISTENER_H */ diff --git a/src/osc/osc/OscPrintReceivedElements.cpp b/src/osc/osc/OscPrintReceivedElements.cpp new file mode 100755 index 0000000000000000000000000000000000000000..430cf924b82d95c060c6b92af96f842c7a68ccf6 --- /dev/null +++ b/src/osc/osc/OscPrintReceivedElements.cpp @@ -0,0 +1,241 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "OscPrintReceivedElements.h" + +#include <iostream> +#include <iomanip> +#include <ctime> +#include <string.h> + + +namespace osc{ + + +std::ostream& operator<<( std::ostream & os, + const ReceivedMessageArgument& arg ) +{ + switch( arg.TypeTag() ){ + case TRUE_TYPE_TAG: + os << "bool:true"; + break; + + case FALSE_TYPE_TAG: + os << "bool:false"; + break; + + case NIL_TYPE_TAG: + os << "(Nil)"; + break; + + case INFINITUM_TYPE_TAG: + os << "(Infinitum)"; + break; + + case INT32_TYPE_TAG: + os << "int32:" << arg.AsInt32Unchecked(); + break; + + case FLOAT_TYPE_TAG: + os << "float32:" << arg.AsFloatUnchecked(); + break; + + case CHAR_TYPE_TAG: + { + char s[2] = {0}; + s[0] = arg.AsCharUnchecked(); + os << "char:'" << s << "'"; + } + break; + + case RGBA_COLOR_TYPE_TAG: + { + uint32 color = arg.AsRgbaColorUnchecked(); + + os << "RGBA:0x" + << std::hex << std::setfill('0') + << std::setw(2) << (int)((color>>24) & 0xFF) + << std::setw(2) << (int)((color>>16) & 0xFF) + << std::setw(2) << (int)((color>>8) & 0xFF) + << std::setw(2) << (int)(color & 0xFF) + << std::setfill(' '); + os.unsetf(std::ios::basefield); + } + break; + + case MIDI_MESSAGE_TYPE_TAG: + { + uint32 m = arg.AsMidiMessageUnchecked(); + os << "midi (port, status, data1, data2):<<" + << std::hex << std::setfill('0') + << "0x" << std::setw(2) << (int)((m>>24) & 0xFF) + << " 0x" << std::setw(2) << (int)((m>>16) & 0xFF) + << " 0x" << std::setw(2) << (int)((m>>8) & 0xFF) + << " 0x" << std::setw(2) << (int)(m & 0xFF) + << std::setfill(' ') << ">>"; + os.unsetf(std::ios::basefield); + } + break; + + case INT64_TYPE_TAG: + os << "int64:" << arg.AsInt64Unchecked(); + break; + + case TIME_TAG_TYPE_TAG: + { + os << "OSC-timetag:" << arg.AsTimeTagUnchecked(); + + std::time_t t = + (unsigned long)( arg.AsTimeTagUnchecked() >> 32 ); + + // strip trailing newline from string returned by ctime + const char *timeString = std::ctime( &t ); + size_t len = strlen( timeString ); + char *s = new char[ len + 1 ]; + strcpy( s, timeString ); + if( len ) + s[ len - 1 ] = '\0'; + + os << " " << s; + } + break; + + case DOUBLE_TYPE_TAG: + os << "double:" << arg.AsDoubleUnchecked(); + break; + + case STRING_TYPE_TAG: + os << "OSC-string:`" << arg.AsStringUnchecked() << "'"; + break; + + case SYMBOL_TYPE_TAG: + os << "OSC-string (symbol):`" << arg.AsSymbolUnchecked() << "'"; + break; + + case BLOB_TYPE_TAG: + { + unsigned long size; + const void *data; + arg.AsBlobUnchecked( data, size ); + os << "OSC-blob:<<" << std::hex << std::setfill('0'); + unsigned char *p = (unsigned char*)data; + for( unsigned long i = 0; i < size; ++i ){ + os << "0x" << std::setw(2) << int(p[i]); + if( i != size-1 ) + os << ' '; + } + os.unsetf(std::ios::basefield); + os << ">>" << std::setfill(' '); + } + break; + + default: + os << "unknown"; + } + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ) +{ + + os << "[" << m.AddressPattern(); + bool first = true; + + for( ReceivedMessage::const_iterator i = m.ArgumentsBegin(); + i != m.ArgumentsEnd(); ++i ){ + if( first ){ + os << " "; + first = false; + }else{ + os << ", "; + } + + os << *i; + } + + os << "]"; + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ) +{ + static int indent = 0; + + for( int j=0; j < indent; ++j ) + os << " "; + os << "{ ( "; + if( b.TimeTag() == 1 ) + os << "immediate"; + else + os << b.TimeTag(); + os << " )\n"; + + ++indent; + + for( ReceivedBundle::const_iterator i = b.ElementsBegin(); + i != b.ElementsEnd(); ++i ){ + if( i->IsBundle() ){ + ReceivedBundle b(*i); + os << b << "\n"; + }else{ + ReceivedMessage m(*i); + for( int j=0; j < indent; ++j ) + os << " "; + os << m << "\n"; + } + } + + --indent; + + for( int j=0; j < indent; ++j ) + os << " "; + os << "}"; + + return os; +} + + +std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ) +{ + if( p.IsBundle() ){ + ReceivedBundle b(p); + os << b << "\n"; + }else{ + ReceivedMessage m(p); + os << m << "\n"; + } + + return os; +} + +} // namespace osc diff --git a/src/osc/osc/OscPrintReceivedElements.h b/src/osc/osc/OscPrintReceivedElements.h new file mode 100755 index 0000000000000000000000000000000000000000..c42cfa56a162432329d0db6d7c45904f3fa6a7ab --- /dev/null +++ b/src/osc/osc/OscPrintReceivedElements.h @@ -0,0 +1,49 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCPRINTRECEIVEDELEMENTS_H +#define INCLUDED_OSCPRINTRECEIVEDELEMENTS_H + +#include <iosfwd> + +#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H +#include "OscReceivedElements.h" +#endif /* INCLUDED_OSCRECEIVEDELEMENTS_H */ + + +namespace osc{ + +std::ostream& operator<<( std::ostream & os, const ReceivedPacket& p ); +std::ostream& operator<<( std::ostream & os, const ReceivedMessageArgument& arg ); +std::ostream& operator<<( std::ostream & os, const ReceivedMessage& m ); +std::ostream& operator<<( std::ostream & os, const ReceivedBundle& b ); + +} // namespace osc + +#endif /* INCLUDED_OSCPRINTRECEIVEDELEMENTS_H */ diff --git a/src/osc/osc/OscReceivedElements.cpp b/src/osc/osc/OscReceivedElements.cpp new file mode 100755 index 0000000000000000000000000000000000000000..326fd62bab6763d29d55f9b8a7b5dfe56af4d65f --- /dev/null +++ b/src/osc/osc/OscReceivedElements.cpp @@ -0,0 +1,722 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "OscReceivedElements.h" + +#include <cassert> + +#include "OscHostEndianness.h" + + +namespace osc{ + + +// return the first 4 byte boundary after the end of a str4 +// be careful about calling this version if you don't know whether +// the string is terminated correctly. +static inline const char* FindStr4End( const char *p ) +{ + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + + while( *p ) + p += 4; + + return p + 1; +} + + +// return the first 4 byte boundary after the end of a str4 +// returns 0 if p == end or if the string is unterminated +static inline const char* FindStr4End( const char *p, const char *end ) +{ + if( p >= end ) + return 0; + + if( p[0] == '\0' ) // special case for SuperCollider integer address pattern + return p + 4; + + p += 3; + end -= 1; + + while( p < end && *p ) + p += 4; + + if( *p ) + return 0; + else + return p + 1; +} + + +static inline unsigned long RoundUp4( unsigned long x ) +{ + unsigned long remainder = x & 0x3UL; + if( remainder ) + return x + (4 - remainder); + else + return x; +} + + +static inline int32 ToInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(int32*)p; +#endif +} + + +static inline uint32 ToUInt32( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint32 i; + char c[4]; + } u; + + u.c[0] = p[3]; + u.c[1] = p[2]; + u.c[2] = p[1]; + u.c[3] = p[0]; + + return u.i; +#else + return *(uint32*)p; +#endif +} + + +int64 ToInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int64 i; + char c[4]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(int64*)p; +#endif +} + + +uint64 ToUInt64( const char *p ) +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::uint64 i; + char c[4]; + } u; + + u.c[0] = p[7]; + u.c[1] = p[6]; + u.c[2] = p[5]; + u.c[3] = p[4]; + u.c[4] = p[3]; + u.c[5] = p[2]; + u.c[6] = p[1]; + u.c[7] = p[0]; + + return u.i; +#else + return *(uint64*)p; +#endif +} + +//------------------------------------------------------------------------------ + +bool ReceivedPacket::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + +//------------------------------------------------------------------------------ + +bool ReceivedBundleElement::IsBundle() const +{ + return (Size() > 0 && Contents()[0] == '#'); +} + + +int32 ReceivedBundleElement::Size() const +{ + return ToUInt32( size_ ); +} + +//------------------------------------------------------------------------------ + +bool ReceivedMessageArgument::AsBool() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TRUE_TYPE_TAG ) + return true; + else if( *typeTag_ == FALSE_TYPE_TAG ) + return false; + else + throw WrongArgumentTypeException(); +} + + +bool ReceivedMessageArgument::AsBoolUnchecked() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TRUE_TYPE_TAG ) + return true; + else + return false; +} + + +int32 ReceivedMessageArgument::AsInt32() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == INT32_TYPE_TAG ) + return AsInt32Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int32 ReceivedMessageArgument::AsInt32Unchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + osc::int32 i; + char c[4]; + } u; + + u.c[0] = argument_[3]; + u.c[1] = argument_[2]; + u.c[2] = argument_[1]; + u.c[3] = argument_[0]; + + return u.i; +#else + return *(int32*)argument_; +#endif +} + + +float ReceivedMessageArgument::AsFloat() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == FLOAT_TYPE_TAG ) + return AsFloatUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +float ReceivedMessageArgument::AsFloatUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + float f; + char c[4]; + } u; + + u.c[0] = argument_[3]; + u.c[1] = argument_[2]; + u.c[2] = argument_[1]; + u.c[3] = argument_[0]; + + return u.f; +#else + return *(float*)argument_; +#endif +} + + +char ReceivedMessageArgument::AsChar() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == CHAR_TYPE_TAG ) + return AsCharUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +char ReceivedMessageArgument::AsCharUnchecked() const +{ + return (char)ToInt32( argument_ ); +} + + +uint32 ReceivedMessageArgument::AsRgbaColor() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == RGBA_COLOR_TYPE_TAG ) + return AsRgbaColorUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsRgbaColorUnchecked() const +{ + return ToUInt32( argument_ ); +} + + +uint32 ReceivedMessageArgument::AsMidiMessage() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == MIDI_MESSAGE_TYPE_TAG ) + return AsMidiMessageUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint32 ReceivedMessageArgument::AsMidiMessageUnchecked() const +{ + return ToUInt32( argument_ ); +} + + +int64 ReceivedMessageArgument::AsInt64() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == INT64_TYPE_TAG ) + return AsInt64Unchecked(); + else + throw WrongArgumentTypeException(); +} + + +int64 ReceivedMessageArgument::AsInt64Unchecked() const +{ + return ToInt64( argument_ ); +} + + +uint64 ReceivedMessageArgument::AsTimeTag() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == TIME_TAG_TYPE_TAG ) + return AsTimeTagUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +uint64 ReceivedMessageArgument::AsTimeTagUnchecked() const +{ + return ToUInt64( argument_ ); +} + + +double ReceivedMessageArgument::AsDouble() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == DOUBLE_TYPE_TAG ) + return AsDoubleUnchecked(); + else + throw WrongArgumentTypeException(); +} + + +double ReceivedMessageArgument::AsDoubleUnchecked() const +{ +#ifdef OSC_HOST_LITTLE_ENDIAN + union{ + double d; + char c[8]; + } u; + + u.c[0] = argument_[7]; + u.c[1] = argument_[6]; + u.c[2] = argument_[5]; + u.c[3] = argument_[4]; + u.c[4] = argument_[3]; + u.c[5] = argument_[2]; + u.c[6] = argument_[1]; + u.c[7] = argument_[0]; + + return u.d; +#else + return *(double*)argument_; +#endif +} + + +const char* ReceivedMessageArgument::AsString() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == STRING_TYPE_TAG ) + return argument_; + else + throw WrongArgumentTypeException(); +} + + +const char* ReceivedMessageArgument::AsSymbol() const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == SYMBOL_TYPE_TAG ) + return argument_; + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlob( const void*& data, unsigned long& size ) const +{ + if( !typeTag_ ) + throw MissingArgumentException(); + else if( *typeTag_ == BLOB_TYPE_TAG ) + AsBlobUnchecked( data, size ); + else + throw WrongArgumentTypeException(); +} + + +void ReceivedMessageArgument::AsBlobUnchecked( const void*& data, unsigned long& size ) const +{ + size = ToUInt32( argument_ ); + data = (void*)(argument_+4); +} + +//------------------------------------------------------------------------------ + +void ReceivedMessageArgumentIterator::Advance() +{ + if( !value_.typeTag_ ) + return; + + switch( *value_.typeTag_++ ){ + case '\0': + // don't advance past end + --value_.typeTag_; + break; + + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + + // zero length + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + value_.argument_ += 4; + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + value_.argument_ += 8; + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + // we use the unsafe function FindStr4End(char*) here because all of + // the arguments have already been validated in + // ReceivedMessage::Init() below. + + value_.argument_ = FindStr4End( value_.argument_ ); + break; + + case BLOB_TYPE_TAG: + { + uint32 blobSize = ToUInt32( value_.argument_ ); + value_.argument_ = value_.argument_ + 4 + RoundUp4( blobSize ); + } + break; + + default: // unknown type tag + // don't advance + --value_.typeTag_; + break; + + + // not handled: + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + } +} + +//------------------------------------------------------------------------------ + +ReceivedMessage::ReceivedMessage( const ReceivedPacket& packet ) + : addressPattern_( packet.Contents() ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedMessage::ReceivedMessage( const ReceivedBundleElement& bundleElement ) + : addressPattern_( bundleElement.Contents() ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +bool ReceivedMessage::AddressPatternIsUInt32() const +{ + return (addressPattern_[0] == '\0'); +} + + +uint32 ReceivedMessage::AddressPatternAsUInt32() const +{ + return ToUInt32( addressPattern_ ); +} + + +void ReceivedMessage::Init( const char *message, unsigned long size ) +{ + if( size == 0 ) + throw MalformedMessageException( "zero length messages not permitted" ); + + if( (size & 0x03L) != 0 ) + throw MalformedMessageException( "message size must be multiple of four" ); + + const char *end = message + size; + + typeTagsBegin_ = FindStr4End( addressPattern_, end ); + if( typeTagsBegin_ == 0 ){ + // address pattern was not terminated before end + throw MalformedMessageException( "unterminated address pattern" ); + } + + if( typeTagsBegin_ == end ){ + // message consists of only the address pattern - no arguments or type tags. + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + if( *typeTagsBegin_ != ',' ) + throw MalformedMessageException( "type tags not present" ); + + if( *(typeTagsBegin_ + 1) == '\0' ){ + // zero length type tags + typeTagsBegin_ = 0; + typeTagsEnd_ = 0; + arguments_ = 0; + + }else{ + // check that all arguments are present and well formed + + arguments_ = FindStr4End( typeTagsBegin_, end ); + if( arguments_ == 0 ){ + throw MalformedMessageException( "type tags were not terminated before end of message" ); + } + + ++typeTagsBegin_; // advance past initial ',' + + const char *typeTag = typeTagsBegin_; + const char *argument = arguments_; + + do{ + switch( *typeTag ){ + case TRUE_TYPE_TAG: + case FALSE_TYPE_TAG: + case NIL_TYPE_TAG: + case INFINITUM_TYPE_TAG: + + // zero length + break; + + case INT32_TYPE_TAG: + case FLOAT_TYPE_TAG: + case CHAR_TYPE_TAG: + case RGBA_COLOR_TYPE_TAG: + case MIDI_MESSAGE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 4; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case INT64_TYPE_TAG: + case TIME_TAG_TYPE_TAG: + case DOUBLE_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument += 8; + if( argument > end ) + throw MalformedMessageException( "arguments exceed message size" ); + break; + + case STRING_TYPE_TAG: + case SYMBOL_TYPE_TAG: + + if( argument == end ) + throw MalformedMessageException( "arguments exceed message size" ); + argument = FindStr4End( argument, end ); + if( argument == 0 ) + throw MalformedMessageException( "unterminated string argument" ); + break; + + case BLOB_TYPE_TAG: + { + if( argument + 4 > end ) + MalformedMessageException( "arguments exceed message size" ); + + uint32 blobSize = ToUInt32( argument ); + argument = argument + 4 + RoundUp4( blobSize ); + if( argument > end ) + MalformedMessageException( "arguments exceed message size" ); + } + break; + + default: + throw MalformedMessageException( "unknown type tag" ); + + // not handled: + // [ Indicates the beginning of an array. The tags following are for + // data in the Array until a close brace tag is reached. + // ] Indicates the end of an array. + } + + }while( *++typeTag != '\0' ); + typeTagsEnd_ = typeTag; + } + } +} + +//------------------------------------------------------------------------------ + +ReceivedBundle::ReceivedBundle( const ReceivedPacket& packet ) + : elementCount_( 0 ) +{ + Init( packet.Contents(), packet.Size() ); +} + + +ReceivedBundle::ReceivedBundle( const ReceivedBundleElement& bundleElement ) + : elementCount_( 0 ) +{ + Init( bundleElement.Contents(), bundleElement.Size() ); +} + + +void ReceivedBundle::Init( const char *bundle, unsigned long size ) +{ + if( size < 16 ) + throw MalformedBundleException( "packet too short for bundle" ); + + if( (size & 0x03L) != 0 ) + throw MalformedBundleException( "bundle size must be multiple of four" ); + + if( bundle[0] != '#' + || bundle[1] != 'b' + || bundle[2] != 'u' + || bundle[3] != 'n' + || bundle[4] != 'd' + || bundle[5] != 'l' + || bundle[6] != 'e' + || bundle[7] != '\0' ) + throw MalformedBundleException( "bad bundle address pattern" ); + + end_ = bundle + size; + + timeTag_ = bundle + 8; + + const char *p = timeTag_ + 8; + + while( p < end_ ){ + if( p + 4 > end_ ) + throw MalformedBundleException( "packet too short for elementSize" ); + + uint32 elementSize = ToUInt32( p ); + if( (elementSize & 0x03L) != 0 ) + throw MalformedBundleException( "bundle element size must be multiple of four" ); + + p += 4 + elementSize; + if( p > end_ ) + throw MalformedBundleException( "packet too short for bundle element" ); + + ++elementCount_; + } + + if( p != end_ ) + throw MalformedBundleException( "bundle contents " ); +} + + +uint64 ReceivedBundle::TimeTag() const +{ + return ToUInt64( timeTag_ ); +} + + +} // namespace osc + diff --git a/src/osc/osc/OscReceivedElements.h b/src/osc/osc/OscReceivedElements.h new file mode 100755 index 0000000000000000000000000000000000000000..f43875772fada240191f6b5260a508ceb7961814 --- /dev/null +++ b/src/osc/osc/OscReceivedElements.h @@ -0,0 +1,486 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCRECEIVEDELEMENTS_H +#define INCLUDED_OSCRECEIVEDELEMENTS_H + +#include "OscTypes.h" +#include "OscException.h" + + +namespace osc{ + + +class MalformedMessageException : public Exception{ +public: + MalformedMessageException( const char *w="malformed message" ) + : Exception( w ) {} +}; + +class MalformedBundleException : public Exception{ +public: + MalformedBundleException( const char *w="malformed bundle" ) + : Exception( w ) {} +}; + +class WrongArgumentTypeException : public Exception{ +public: + WrongArgumentTypeException( const char *w="wrong argument type" ) + : Exception( w ) {} +}; + +class MissingArgumentException : public Exception{ +public: + MissingArgumentException( const char *w="missing argument" ) + : Exception( w ) {} +}; + +class ExcessArgumentException : public Exception{ +public: + ExcessArgumentException( const char *w="too many arguments" ) + : Exception( w ) {} +}; + + +class ReceivedPacket{ +public: + ReceivedPacket( const char *contents, int32 size ) + : contents_( contents ) + , size_( size ) {} + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + int32 Size() const { return size_; } + const char *Contents() const { return contents_; } + +private: + const char *contents_; + int32 size_; +}; + + +class ReceivedBundleElement{ +public: + ReceivedBundleElement( const char *size ) + : size_( size ) {} + + friend class ReceivedBundleElementIterator; + + bool IsMessage() const { return !IsBundle(); } + bool IsBundle() const; + + int32 Size() const; + const char *Contents() const { return size_ + 4; } + +private: + const char *size_; +}; + + +class ReceivedBundleElementIterator{ +public: + ReceivedBundleElementIterator( const char *sizePtr ) + : value_( sizePtr ) {} + + ReceivedBundleElementIterator operator++() + { + Advance(); + return *this; + } + + ReceivedBundleElementIterator operator++(int) + { + ReceivedBundleElementIterator old( *this ); + Advance(); + return old; + } + + const ReceivedBundleElement& operator*() const { return value_; } + + const ReceivedBundleElement* operator->() const { return &value_; } + + friend bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ); + +private: + ReceivedBundleElement value_; + + void Advance() { value_.size_ = value_.Contents() + value_.Size(); } + + bool IsEqualTo( const ReceivedBundleElementIterator& rhs ) const + { + return value_.size_ == rhs.value_.size_; + } +}; + +inline bool operator==(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedBundleElementIterator& lhs, + const ReceivedBundleElementIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgument{ +public: + ReceivedMessageArgument( const char *typeTag, const char *argument ) + : typeTag_( typeTag ) + , argument_( argument ) {} + + friend class ReceivedMessageArgumentIterator; + + const char TypeTag() const { return *typeTag_; } + + // the unchecked methods below don't check whether the argument actually + // is of the specified type. they should only be used if you've already + // checked the type tag or the associated IsType() method. + + bool IsBool() const + { return *typeTag_ == TRUE_TYPE_TAG || *typeTag_ == FALSE_TYPE_TAG; } + bool AsBool() const; + bool AsBoolUnchecked() const; + + bool IsNil() const { return *typeTag_ == NIL_TYPE_TAG; } + bool IsInfinitum() const { return *typeTag_ == INFINITUM_TYPE_TAG; } + + bool IsInt32() const { return *typeTag_ == INT32_TYPE_TAG; } + int32 AsInt32() const; + int32 AsInt32Unchecked() const; + + bool IsFloat() const { return *typeTag_ == FLOAT_TYPE_TAG; } + float AsFloat() const; + float AsFloatUnchecked() const; + + bool IsChar() const { return *typeTag_ == CHAR_TYPE_TAG; } + char AsChar() const; + char AsCharUnchecked() const; + + bool IsRgbaColor() const { return *typeTag_ == RGBA_COLOR_TYPE_TAG; } + uint32 AsRgbaColor() const; + uint32 AsRgbaColorUnchecked() const; + + bool IsMidiMessage() const { return *typeTag_ == MIDI_MESSAGE_TYPE_TAG; } + uint32 AsMidiMessage() const; + uint32 AsMidiMessageUnchecked() const; + + bool IsInt64() const { return *typeTag_ == INT64_TYPE_TAG; } + int64 AsInt64() const; + int64 AsInt64Unchecked() const; + + bool IsTimeTag() const { return *typeTag_ == TIME_TAG_TYPE_TAG; } + uint64 AsTimeTag() const; + uint64 AsTimeTagUnchecked() const; + + bool IsDouble() const { return *typeTag_ == DOUBLE_TYPE_TAG; } + double AsDouble() const; + double AsDoubleUnchecked() const; + + bool IsString() const { return *typeTag_ == STRING_TYPE_TAG; } + const char* AsString() const; + const char* AsStringUnchecked() const { return argument_; } + + bool IsSymbol() const { return *typeTag_ == SYMBOL_TYPE_TAG; } + const char* AsSymbol() const; + const char* AsSymbolUnchecked() const { return argument_; } + + bool IsBlob() const { return *typeTag_ == BLOB_TYPE_TAG; } + void AsBlob( const void*& data, unsigned long& size ) const; + void AsBlobUnchecked( const void*& data, unsigned long& size ) const; + +private: + const char *typeTag_; + const char *argument_; +}; + + +class ReceivedMessageArgumentIterator{ +public: + ReceivedMessageArgumentIterator( const char *typeTags, const char *arguments ) + : value_( typeTags, arguments ) {} + + ReceivedMessageArgumentIterator operator++() + { + Advance(); + return *this; + } + + ReceivedMessageArgumentIterator operator++(int) + { + ReceivedMessageArgumentIterator old( *this ); + Advance(); + return old; + } + + const ReceivedMessageArgument& operator*() const { return value_; } + + const ReceivedMessageArgument* operator->() const { return &value_; } + + friend bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ); + +private: + ReceivedMessageArgument value_; + + void Advance(); + + bool IsEqualTo( const ReceivedMessageArgumentIterator& rhs ) const + { + return value_.typeTag_ == rhs.value_.typeTag_; + } +}; + +inline bool operator==(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return lhs.IsEqualTo( rhs ); +} + +inline bool operator!=(const ReceivedMessageArgumentIterator& lhs, + const ReceivedMessageArgumentIterator& rhs ) +{ + return !( lhs == rhs ); +} + + +class ReceivedMessageArgumentStream{ + friend class ReceivedMessage; + ReceivedMessageArgumentStream( const ReceivedMessageArgumentIterator& begin, + const ReceivedMessageArgumentIterator& end ) + : p_( begin ) + , end_( end ) {} + + ReceivedMessageArgumentIterator p_, end_; + +public: + + // end of stream + bool Eos() const { return p_ == end_; } + + ReceivedMessageArgumentStream& operator>>( bool& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsBool(); + return *this; + } + + // not sure if it would be useful to stream Nil and Infinitum + // for now it's not possible + + ReceivedMessageArgumentStream& operator>>( int32& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt32(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( float& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsFloat(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( char& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsChar(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( RgbaColor& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsRgbaColor(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MidiMessage& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsMidiMessage(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( int64& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsInt64(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( TimeTag& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsTimeTag(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( double& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsDouble(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Blob& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + (*p_++).AsBlob( rhs.data, rhs.size ); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( const char*& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs = (*p_++).AsString(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( Symbol& rhs ) + { + if( Eos() ) + throw MissingArgumentException(); + + rhs.value = (*p_++).AsSymbol(); + return *this; + } + + ReceivedMessageArgumentStream& operator>>( MessageTerminator& rhs ) + { + if( !Eos() ) + throw ExcessArgumentException(); + + return *this; + } +}; + + +class ReceivedMessage{ + void Init( const char *bundle, unsigned long size ); +public: + explicit ReceivedMessage( const ReceivedPacket& packet ); + explicit ReceivedMessage( const ReceivedBundleElement& bundleElement ); + + const char *AddressPattern() const { return addressPattern_; } + + // Support for non-standad SuperCollider integer address patterns: + bool AddressPatternIsUInt32() const; + uint32 AddressPatternAsUInt32() const; + + unsigned long ArgumentCount() const { return static_cast<unsigned long>(typeTagsEnd_ - typeTagsBegin_); } + + const char *TypeTags() const { return typeTagsBegin_; } + + + typedef ReceivedMessageArgumentIterator const_iterator; + + ReceivedMessageArgumentIterator ArgumentsBegin() const + { + return ReceivedMessageArgumentIterator( typeTagsBegin_, arguments_ ); + } + + ReceivedMessageArgumentIterator ArgumentsEnd() const + { + return ReceivedMessageArgumentIterator( typeTagsEnd_, 0 ); + } + + ReceivedMessageArgumentStream ArgumentStream() const + { + return ReceivedMessageArgumentStream( ArgumentsBegin(), ArgumentsEnd() ); + } + +private: + const char *addressPattern_; + const char *typeTagsBegin_; + const char *typeTagsEnd_; + const char *arguments_; +}; + + +class ReceivedBundle{ + void Init( const char *message, unsigned long size ); +public: + explicit ReceivedBundle( const ReceivedPacket& packet ); + explicit ReceivedBundle( const ReceivedBundleElement& bundleElement ); + + uint64 TimeTag() const; + + unsigned long ElementCount() const { return elementCount_; } + + typedef ReceivedBundleElementIterator const_iterator; + + ReceivedBundleElementIterator ElementsBegin() const + { + return ReceivedBundleElementIterator( timeTag_ + 8 ); + } + + ReceivedBundleElementIterator ElementsEnd() const + { + return ReceivedBundleElementIterator( end_ ); + } + +private: + const char *timeTag_; + const char *end_; + unsigned long elementCount_; +}; + + +} // namespace osc + + +#endif /* INCLUDED_OSCRECEIVEDELEMENTS_H */ diff --git a/src/osc/osc/OscTypes.cpp b/src/osc/osc/OscTypes.cpp new file mode 100755 index 0000000000000000000000000000000000000000..889ab43299700bd45eb88c5417f17d955f808e09 --- /dev/null +++ b/src/osc/osc/OscTypes.cpp @@ -0,0 +1,40 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#include "OscTypes.h" + +namespace osc{ + +BundleInitiator BeginBundleImmediate(1); +BundleTerminator EndBundle; +MessageTerminator EndMessage; +NilType Nil; +InfinitumType Infinitum; + +} // namespace osc diff --git a/src/osc/osc/OscTypes.h b/src/osc/osc/OscTypes.h new file mode 100755 index 0000000000000000000000000000000000000000..81549b5fe58eba0611c3675b7c4a84536cafcc59 --- /dev/null +++ b/src/osc/osc/OscTypes.h @@ -0,0 +1,178 @@ +/* + oscpack -- Open Sound Control packet manipulation library + http://www.audiomulch.com/~rossb/oscpack + + Copyright (c) 2004-2005 Ross Bencina <rossb@audiomulch.com> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + requested to send the modifications to the original developer so that + they can be incorporated into the canonical version. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +#ifndef INCLUDED_OSCTYPES_H +#define INCLUDED_OSCTYPES_H + + +namespace osc{ + +// basic types + +#if defined(__BORLANDC__) || defined(_MSC_VER) + +typedef __int64 int64; +typedef unsigned __int64 uint64; + +#else + +typedef long long int64; +typedef unsigned long long uint64; + +#endif + + + +#ifdef x86_64 + +typedef signed int int32; +typedef unsigned int uint32; + +#else + +typedef signed long int32; +typedef unsigned long uint32; + +#endif + + + +enum TypeTagValues { + TRUE_TYPE_TAG = 'T', + FALSE_TYPE_TAG = 'F', + NIL_TYPE_TAG = 'N', + INFINITUM_TYPE_TAG = 'I', + INT32_TYPE_TAG = 'i', + FLOAT_TYPE_TAG = 'f', + CHAR_TYPE_TAG = 'c', + RGBA_COLOR_TYPE_TAG = 'r', + MIDI_MESSAGE_TYPE_TAG = 'm', + INT64_TYPE_TAG = 'h', + TIME_TAG_TYPE_TAG = 't', + DOUBLE_TYPE_TAG = 'd', + STRING_TYPE_TAG = 's', + SYMBOL_TYPE_TAG = 'S', + BLOB_TYPE_TAG = 'b' +}; + + + +// i/o manipulators used for streaming interfaces + +struct BundleInitiator{ + explicit BundleInitiator( uint64 timeTag_ ) : timeTag( timeTag_ ) {} + uint64 timeTag; +}; + +extern BundleInitiator BeginBundleImmediate; + +inline BundleInitiator BeginBundle( uint64 timeTag=1 ) +{ + return BundleInitiator(timeTag); +} + + +struct BundleTerminator{ +}; + +extern BundleTerminator EndBundle; + +struct BeginMessage{ + explicit BeginMessage( const char *addressPattern_ ) : addressPattern( addressPattern_ ) {} + const char *addressPattern; +}; + +struct MessageTerminator{ +}; + +extern MessageTerminator EndMessage; + + +// osc specific types. they are defined as structs so they can be used +// as separately identifiable types with the streaming operators. + +struct NilType{ +}; + +extern NilType Nil; + + +struct InfinitumType{ +}; + +extern InfinitumType Infinitum; + +struct RgbaColor{ + RgbaColor() {} + explicit RgbaColor( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct MidiMessage{ + MidiMessage() {} + explicit MidiMessage( uint32 value_ ) : value( value_ ) {} + uint32 value; + + operator uint32() const { return value; } +}; + + +struct TimeTag{ + TimeTag() {} + explicit TimeTag( uint64 value_ ) : value( value_ ) {} + uint64 value; + + operator uint64() const { return value; } +}; + + +struct Symbol{ + Symbol() {} + explicit Symbol( const char* value_ ) : value( value_ ) {} + const char* value; + + operator const char *() const { return value; } +}; + + +struct Blob{ + Blob() {} + explicit Blob( const void* data_, unsigned long size_ ) + : data( data_ ), size( size_ ) {} + const void* data; + unsigned long size; +}; + +} // namespace osc + + +#endif /* INCLUDED_OSCTYPES_H */ diff --git a/src/win/WinWindow.cpp b/src/win/WinWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56204066e0671a8334dfd1b3ed61342c1def93ea --- /dev/null +++ b/src/win/WinWindow.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * MacWindow.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "MacWindow.hpp" +#include <iostream> + +using namespace std; + +MacWindow::MacWindow(OvWindowsManager* man, CGWindowID id): OvWindow(man) { + CGRect rect = CGRectMake(0, 0, 100, 100); + m_idCArray[0] = id; + m_idArray = CFArrayCreate(NULL, (const void **) m_idCArray, 1, NULL); + CFArrayRef descArray = CGWindowListCreateDescriptionFromArray(m_idArray); + if(CFArrayGetCount(descArray)>0) { + CFDictionaryRef description = + (CFDictionaryRef) CFArrayGetValueAtIndex(descArray, 0); + if (CFDictionaryContainsKey(description, kCGWindowBounds)) { + CFDictionaryRef bounds = + (CFDictionaryRef) CFDictionaryGetValue(description, + kCGWindowBounds); + if (bounds) { + CGRectMakeWithDictionaryRepresentation(bounds, &rect); + } + } + } + CFRelease(descArray); + + m_posX = rect.origin.x; + m_posY = rect.origin.y; + m_width = rect.size.width; + m_height = rect.size.height; + m_offsetX = m_posX; + m_offsetY = m_posY; + m_pixPerRow = m_width; + m_isBGR=true; + m_needsOffset=false; + m_pixPerRow=m_width; +} + +MacWindow::~MacWindow() { + CFRelease(m_idArray); +} + +int MacWindow::computeNbPixPerRow(const int& srcW, const int& srcH) { + CGRect rect = CGRectMake(m_posX, m_posY, srcW, srcH); + CGImageRef img = CGWindowListCreateImageFromArray(rect, + m_idArray, + kCGWindowImageBoundsIgnoreFraming); + int nb = CGImageGetBytesPerRow(img)/4; + CFRelease(img); + return nb; +} + +void MacWindow::getPixels(const int& srcX, const int& srcY, + const int& srcW, const int& srcH, + const vector<int>& srcIndices, + const vector<vector<int> >& srcCoords, + const vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg) { + CGRect rect = CGRectMake(m_posX+srcX, m_posY+srcY, srcW, srcH); + CGImageRef img = CGWindowListCreateImageFromArray(rect, + m_idArray, + kCGWindowImageBoundsIgnoreFraming); + if(img!=NULL) { + CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(img)); + uchar* buf = (uchar*)CFDataGetBytePtr(data); + vector<int>::const_iterator itId = srcIndices.begin(); + vector<vector<int> >::const_iterator itCo = srcCoords.begin(); + vector<int>::const_iterator itDe = destIndices.begin(); + for(; itCo!=srcCoords.end(); ++itCo) { + if((*itCo)[0]>=0) { + //copy rgb + for(int c=0; c<3; ++c) { + destImg[(*itDe)] = (color==ZoneWidget::NORMAL) + ?buf[(*itId)] + :255-buf[(*itId)]; + ++itId; + ++itDe; + } + //copy alpha + destImg[(*itDe)] = alpha; + ++itId; + ++itDe; + } + else { //black + for(int c=0; c<4; ++c) { + destImg[(*itDe)] = 0; + ++itId; + ++itDe; + } + } + } + //release CG stuff + CFRelease(data); + CFRelease(img); + } + else { + cout<<"Error getting image of window "<<m_name<<endl; + } +} + diff --git a/src/win/WinWindow.hpp b/src/win/WinWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2c2cfb02b7d15cf7c9750a04a0b8ebf86dbf181f --- /dev/null +++ b/src/win/WinWindow.hpp @@ -0,0 +1,53 @@ +/*************************************************************************** + * MacWindow.hpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef MacWindow_h +#define MacWindow_h + +#include <vector> +#include <string> + +#include "../OvWindow.hpp" +#import <ApplicationServices/ApplicationServices.h> + +class MacWindow: public OvWindow { + public : + MacWindow(OvWindowsManager* man, CGWindowID winID); + virtual ~MacWindow(); + virtual void getPixels(const int& x, const int& y, + const int& sx, const int& sy, + const std::vector<int>& srcIndices, + const std::vector<std::vector<int> >& srcCoords, + const std::vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg); + int computeNbPixPerRow(const int& srcW, const int& srcH); + + protected: + CGWindowID m_idCArray[1]; + CFArrayRef m_idArray; +}; + +#endif + diff --git a/src/win/WinWindowsManager.cpp b/src/win/WinWindowsManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a4e6a6478bbc18060e7713fb5146957dd3ff4d0 --- /dev/null +++ b/src/win/WinWindowsManager.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * MacWindowsManager.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Relies on code from: + https://github.com/JoelSjogren/window-copy/blob/master/windowutils.cpp +*/ + +#include "MacWindowsManager.hpp" +#include <iostream> +#include <stdlib.h> + +using namespace std; + +MacWindowsManager::MacWindowsManager(): OvWindowsManager() { + +} + +MacWindowsManager::~MacWindowsManager() { + +} + +void MacWindowsManager::updateWindowsList() { + m_windowList.clear(); + CFArrayRef windowArray = + CGWindowListCreate(kCGWindowListExcludeDesktopElements + |kCGWindowListOptionOnScreenOnly, kCGNullWindowID); + CFArrayRef descArray = CGWindowListCreateDescriptionFromArray(windowArray); + if(descArray!=NULL) { + for(int i=0; i<CFArrayGetCount(descArray); ++i) { + CFDictionaryRef win = + (CFDictionaryRef)CFArrayGetValueAtIndex(descArray,i); + CFStringRef name = + (CFStringRef) CFDictionaryGetValue(win, kCGWindowName); + if(name!=NULL) { + if(CFStringGetLength(name)>0) { + char tempStr[128]; + CFStringGetCString(name,tempStr,128,kCFStringEncodingUTF8); + string nameStr(tempStr); + MacWindow* newWin = + new MacWindow(this, + (CGWindowID)(uintptr_t) + CFArrayGetValueAtIndex(windowArray,i)); + newWin->setName(nameStr); + m_windowList.push_back(newWin); + } + } + CFRelease(win); + } + } + else { + cout<<"Error: No windows found"<<endl; + } +} + + diff --git a/src/win/WinWindowsManager.hpp b/src/win/WinWindowsManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fd5de79463d4ef76706ad21e090ec86e99f796d7 --- /dev/null +++ b/src/win/WinWindowsManager.hpp @@ -0,0 +1,42 @@ +/*************************************************************************** + * MacWindowsManager.hpp + * Part of Over + * 2013 Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef MacWindowsManager_h +#define MacWindowsManager_h + +#include <vector> +#include <string> + +#include "../OvWindowsManager.hpp" +#include "MacWindow.hpp" + +class MacWindowsManager : public OvWindowsManager { + public : + MacWindowsManager(); + virtual ~MacWindowsManager(); + virtual void updateWindowsList(); + protected: +}; + +#endif + diff --git a/src/x11/XWindow.cpp b/src/x11/XWindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1eb557b163d97c984d0ef73bfcade1fe16be171 --- /dev/null +++ b/src/x11/XWindow.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + * XWindow.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "XWindow.hpp" +#include <iostream> + +using namespace std; + +XWindow::XWindow(CutWindowsManager* man, + Display* disp, + const Window& win): CutWindow(man), + m_disp(disp), + m_win(win) { + Window root; + unsigned int bw, d, w, h; + XGetGeometry(m_disp, m_win, &root, &m_posX, &m_posY, + &w, &h, &bw, &d); + m_width=w; + m_height=h; + m_offsetX=0; + m_offsetY=0; + m_pixPerRow=m_width; + m_isBGR=false; + m_needsOffset=true; + m_grabbed=false; + m_defaultImgData = new uchar[m_width*m_height*3]; +} + +int XWindow::computeNbPixPerRow(const int& srcW, const int& srcH) { + XWindowAttributes attrs; + XGetWindowAttributes(m_disp, m_win, &attrs); + m_width=attrs.width; + m_height=attrs.height; + m_pixPerRow=m_width; + return m_width; +} + +void XWindow::getPixels(const int& srcX, const int& srcY, + const int& srcW, const int& srcH, + const vector<int>& srcIndices, + const vector<vector<int> >& srcCoords, + const vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg) { + XImage* ximg = XGetImage(m_disp, m_win, + srcX, srcY, + srcW, srcH, + AllPlanes, XYPixmap); + if(ximg!=NULL) { + unsigned long red_mask = ximg->red_mask; + unsigned long green_mask = ximg->green_mask; + unsigned long blue_mask = ximg->blue_mask; + + vector<int>::const_iterator itId = srcIndices.begin(); + vector<vector<int> >::const_iterator itCo = srcCoords.begin(); + vector<int>::const_iterator itDe = destIndices.begin(); + for(; itCo!=srcCoords.end(); ++itCo) { + if((*itCo)[0]>=0) {//get and copy pixel + unsigned long pixel = XGetPixel(ximg, (*itCo)[0], (*itCo)[1]); + destImg[(*itDe)] = (color==ZoneWidget::NORMAL) + ?(pixel & red_mask) >> 16 + :255-((pixel & red_mask) >> 16); + ++itDe; + destImg[(*itDe)] = (color==ZoneWidget::NORMAL) + ?(pixel & green_mask) >> 8 + :255-((pixel & green_mask) >> 8); + ++itDe; + destImg[(*itDe)] = (color==ZoneWidget::NORMAL) + ?(pixel & blue_mask) + :255-(pixel & blue_mask); + ++itDe; + destImg[(*itDe)] = alpha; + ++itDe; + } + else { //black + destImg[(*itDe)] = 0; + ++itDe; + destImg[(*itDe)] = 0; + ++itDe; + destImg[(*itDe)] = 0; + ++itDe; + destImg[(*itDe)] = 0; + ++itDe; + } + } + XDestroyImage(ximg); + } +} + +void XWindow::releaseImage() { + if(m_grabbed) { + if(m_ximg) { + XDestroyImage(m_ximg); + } + m_grabbed=false; + } +} + +uchar* XWindow::grabImage() { + if(!m_grabbed) { + m_ximg = XGetImage(m_disp, m_win, + 0, 0, + m_width, m_height, + AllPlanes, ZPixmap); + if(m_ximg) { + m_imgData=(uchar*)m_ximg->data; + } + else { + m_imgData=m_defaultImgData; + } + m_grabbed=true; + } + return m_imgData; +} + +void XWindow::storeImage(const int& id) { + int size = m_pixPerRow*m_height*4; + if(m_storedImgData.find(id)!=m_storedImgData.end()) { + delete [] m_storedImgData[id]; + } + m_storedImgData[id] = new uchar[size]; + memcpy(m_storedImgData[id], m_imgData, size); +} + +uchar* XWindow::retrieveImage(const int& id) { + map<int, uchar*>::iterator itSt = m_storedImgData.find(id); + if(itSt!=m_storedImgData.end()) { + return itSt->second; + } + return m_imgData; +} + + + diff --git a/src/x11/XWindow.hpp b/src/x11/XWindow.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0d36a613e9638ecc4d88156fb5325f6c2283af49 --- /dev/null +++ b/src/x11/XWindow.hpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * CutWindow.hpp + * Part of Over + * 2013 Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef XWindow_h +#define XWindow_h + +#include <vector> +#include <string> + +#include "../CutWindow.hpp" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> + +class XWindow: public CutWindow { + public : + XWindow(CutWindowsManager* man, Display* disp, const Window& win); + virtual ~XWindow(){} + virtual void getPixels(const int& x, const int& y, + const int& sx, const int& sy, + const std::vector<int>& srcIndices, + const std::vector<std::vector<int> >& srcCoords, + const std::vector<int>& destIndices, + const uchar& alpha, + const ZoneWidget::ZONE_COLOR& color, + uchar* destImg); + int computeNbPixPerRow(const int& srcW, const int& srcH); + virtual uchar* grabImage(); + virtual void releaseImage(); + + virtual void storeImage(const int& id); + virtual uchar* retrieveImage(const int& id); + + protected: + Display* m_disp; + Window m_win; + XImage* m_ximg; +}; + +#endif + diff --git a/src/x11/XWindowsManager.cpp b/src/x11/XWindowsManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cab2c04d0316c4383a1cfc302419120cf58fedfa --- /dev/null +++ b/src/x11/XWindowsManager.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * XWindowsManager.cpp + * Part of + * 2016- Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "XWindowsManager.hpp" +#include <iostream> +#include <stdlib.h> + +using namespace std; + +XWindowsManager::XWindowsManager(): CutWindowsManager() { + m_disp = XOpenDisplay(NULL); + if (!m_disp) { + cout<<"No display found"<<endl; + } +} + +XWindowsManager::~XWindowsManager() { + XCloseDisplay(m_disp); +} + +void XWindowsManager::updateWindowsList() { + map<string, CutWindow*> prevWins; + vector<CutWindow*>::iterator itWin=m_windowList.begin(); + for(; itWin!=m_windowList.end(); ++itWin) { + prevWins[(*itWin)->getName()]=(*itWin); + } + m_windowList.clear(); + unsigned long len; + Window *list = getWinList(m_disp, &len); + for(int i=0;i<(int)len;i++) { + char* name = getWinName(m_disp, list[i]); + CutWindow* newWin=NULL; + if(prevWins.find(string(name))!=prevWins.end()) { + newWin = prevWins[string(name)]; + } + else { + newWin = new XWindow(this, m_disp, list[i]); + } + newWin->setName(string(name)); + m_windowList.push_back(newWin); + free(name); + } + XFree(list); +} + +char* XWindowsManager::getWinName(Display* disp, Window win) { + Atom prop = XInternAtom(disp,"WM_NAME",False), type; + int form; + unsigned long remain, len; + unsigned char *list; + + if (XGetWindowProperty(disp,win,prop,0,1024,False,XA_STRING, + &type,&form,&len,&remain,&list) != Success) { + cout<<"Error getting window name"<<endl; + return NULL; + } + return (char*)list; +} + +Window* XWindowsManager::getWinList(Display* disp, unsigned long* len) { + Atom prop = XInternAtom(disp,"_NET_CLIENT_LIST",False), type; + int form; + unsigned long remain; + unsigned char *list; + if(XGetWindowProperty(disp,XDefaultRootWindow(disp), + prop,0,1024,False,XA_WINDOW, + &type,&form,len,&remain,&list) != Success) { + cout<<"Error getting windows list"<<endl; + return 0; + } + return (Window*)list; +} + + diff --git a/src/x11/XWindowsManager.hpp b/src/x11/XWindowsManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a5d99e868c98d5f423a7f874093fcc103de51cc1 --- /dev/null +++ b/src/x11/XWindowsManager.hpp @@ -0,0 +1,49 @@ +/*************************************************************************** + * XWindowsManager.hpp + * Part of Over + * 2013 Florent Berthaut + * hitmuri.net + ****************************************************************************/ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef XWindowsManager_h +#define XWindowsManager_h + +#include <vector> +#include <string> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +#include "../CutWindowsManager.hpp" +#include "XWindow.hpp" + +class XWindowsManager : public CutWindowsManager { + public : + XWindowsManager(); + virtual ~XWindowsManager(); + virtual void updateWindowsList(); + + Window* getWinList(Display* disp, unsigned long* len); + char* getWinName(Display* disp, Window); + + protected: + Display* m_disp; +}; + +#endif + diff --git a/waf b/waf new file mode 100755 index 0000000000000000000000000000000000000000..e1e34d431afba28d52add07a6094226a1a74374b Binary files /dev/null and b/waf differ diff --git a/wscript b/wscript new file mode 100755 index 0000000000000000000000000000000000000000..698afce7a08273644b10bcedcaf38bdc96f916c3 --- /dev/null +++ b/wscript @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +import sys, os + +def options(opt): + opt.load('compiler_cxx') + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_cxx') + conf.load('compiler_c') + + #platform specific + if sys.platform == 'darwin': + conf.env.INCLUDES_OS = ['/opt/local/include', + '/opt/local/include/libxml2'] + conf.env.LIBPATH_OS = ['/opt/local/lib'] + conf.env.LIB_OS = ['m', 'xml2', 'fltk', 'fltk_gl', 'portmidi','GLEW'] + conf.env.FRAMEWORK_OS = ['Cocoa','OpenGL', 'AGL', 'Carbon', + 'Accelerate', 'IOKit','System', 'AppKit', + 'CoreFoundation'] + conf.env.DEFINES_OS = ['OSX=1'] + conf.env.DEFINES_GL = ['GL=1'] + elif sys.platform == 'win32' or sys.platform == 'cygwin': + conf.env.INCLUDES_OS = ['os/win/include/', 'C:\MinGW\include'] + conf.env.LIBPATH_OS = [os.path.join(os.getcwd(), 'os/win/lib/')] + conf.env.LIB_OS = ['m', 'ws2_32', 'xml2', 'GLU', 'GL', + 'fltk', 'fltk_gl','boost_system'] + conf.env.DEFINES_OS = ['WINDOWS=1'] + else : + conf.env.INCLUDES_OS = ['/usr/include', '/usr/local/include', + '/usr/include/libxml2'] + conf.env.LIB_OS = ['X11', 'Xxf86vm', 'm','portmidi','porttime', + 'xml2', 'pthread','GL','GLEW', + 'fltk', 'fltk_gl', 'boost_system'] + conf.env.LIBPATH_OS = ['/usr/local/lib/'] + conf.env.DEFINES_OS = ['LINUX=1'] + conf.env.DEFINES_GL = ['GL=1'] + + + #release specific + conf.env.CXXFLAGS = ['-O3', '-Wall'] + conf.env.DEFINES = ['DEBUG(x)=//x'] + + #debug specific + conf.setenv('debug', env=conf.env.derive()) + conf.env.CXXFLAGS = ['-g', '-Wall'] + conf.env.DEFINES = ['DEBUG(x)=std::cout<< x <<std::endl;'] + +def build(bld): + installPath = '/usr/local/bin' + macApp = False + + if sys.platform == 'darwin': + installPath = '/opt/bin/' + macApp = 'True' + osCode = 'src/mac/*.cpp' + elif sys.platform == 'win32' or sys.platform == 'cygwin': + osCode = 'src/win/*.cpp' + else : + osCode = 'src/x11/*.cpp' + + bld.objects( + source = bld.path.ant_glob('src/osc/ip/posix/*.cpp') + +bld.path.ant_glob('src/osc/ip/*.cpp') + +bld.path.ant_glob('src/osc/osc/*.cpp'), + use = ['OS'], + target = 'oscpack') + + bld.objects( + source = bld.path.ant_glob(osCode), + use = ['OS','GL'], + target = 'winsman') + + bld.program( + source = bld.path.ant_glob('src/*.cpp'), + use = ['OS', 'GL', 'winsman', 'oscpack'], + target = 'controllar'+bld.variant, + vnum = '0.0.1', + install_path = installPath, + mac_app = macApp, + mac_plist = 'data/Info.plist', + mac_resources = 'data/controllar.icns', + ) + +def install(bld): + bld.install('${PREFIX}/bin', 'controllar') + +from waflib.Build import BuildContext, CleanContext +class debug(BuildContext): + cmd = 'debug' + variant = 'debug' +