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' 
+