From fe9e38995f5a0abc196e9600c38d95385d6bf84f Mon Sep 17 00:00:00 2001 From: Jonas Suhr Christensen Date: Sat, 18 Jan 2014 10:17:58 +0100 Subject: Added PuGl window drawing. Enable with './configure -with-pugl'. --- INSTALL | 213 ++++++++--- configure.ac | 5 +- plugingui/Makefile.am | 1 + plugingui/Makefile.am.plugingui | 4 +- plugingui/window.cc | 13 +- pugl/.svn/entries | 1 + pugl/.svn/format | 1 + ...38ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base | 10 + ...24cfa41e889fddfea6ab4870872f0057e37373.svn-base | 376 +++++++++++++++++++ ...41e2033558edd90840bdb0cd9904dae31321b5.svn-base | 155 ++++++++ ...55d700ef68e3453f8f13725eb6d436815fee0b.svn-base | Bin 0 -> 92192 bytes ...b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base | 400 +++++++++++++++++++++ ...36d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base | 357 ++++++++++++++++++ ...e5f0088ae9801b2069c0f247160a6c44836112.svn-base | 5 + ...a2a332bc430fd6f006d218654060baa9af14bb.svn-base | 143 ++++++++ ...22c035ba53b96a90015d3789aac71b53267aa5.svn-base | 386 ++++++++++++++++++++ ...dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base | 13 + ...aef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base | 193 ++++++++++ ...9501dc0e0ae55c38b63429be01f2a045d76823.svn-base | 27 ++ pugl/.svn/wc.db | Bin 0 -> 39936 bytes pugl/AUTHORS | 5 + pugl/COPYING | 13 + pugl/README | 27 ++ pugl/pugl.pc.in | 10 + pugl/pugl/pugl.h | 357 ++++++++++++++++++ pugl/pugl/pugl_internal.h | 143 ++++++++ pugl/pugl/pugl_osx.m | 400 +++++++++++++++++++++ pugl/pugl/pugl_win.cpp | 376 +++++++++++++++++++ pugl/pugl/pugl_x11.c | 386 ++++++++++++++++++++ pugl/pugl_test.c | 193 ++++++++++ pugl/waf | Bin 0 -> 92192 bytes pugl/wscript | 155 ++++++++ 32 files changed, 4320 insertions(+), 48 deletions(-) create mode 100644 pugl/.svn/entries create mode 100644 pugl/.svn/format create mode 100644 pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base create mode 100644 pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base create mode 100644 pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base create mode 100644 pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base create mode 100644 pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base create mode 100644 pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base create mode 100644 pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base create mode 100644 pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base create mode 100644 pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base create mode 100644 pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base create mode 100644 pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base create mode 100644 pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base create mode 100644 pugl/.svn/wc.db create mode 100644 pugl/AUTHORS create mode 100644 pugl/COPYING create mode 100644 pugl/README create mode 100644 pugl/pugl.pc.in create mode 100644 pugl/pugl/pugl.h create mode 100644 pugl/pugl/pugl_internal.h create mode 100644 pugl/pugl/pugl_osx.m create mode 100644 pugl/pugl/pugl_win.cpp create mode 100644 pugl/pugl/pugl_x11.c create mode 100644 pugl/pugl_test.c create mode 100755 pugl/waf create mode 100644 pugl/wscript diff --git a/INSTALL b/INSTALL index d3c5b40..007e939 100644 --- a/INSTALL +++ b/INSTALL @@ -1,19 +1,25 @@ Installation Instructions ************************* -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007 Free Software Foundation, Inc. +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. Basic Installation ================== -Briefly, the shell commands `./configure; make; make install' should + Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for -instructions specific to this package. +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses @@ -42,7 +48,7 @@ may remove or edit it. you want to change it or regenerate `configure' using a newer version of `autoconf'. -The simplest way to compile this package is: + The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. @@ -53,12 +59,22 @@ The simplest way to compile this package is: 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with - the package. + the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is @@ -67,15 +83,22 @@ The simplest way to compile this package is: all sorts of other programs in order to regenerate files that came with the distribution. - 6. Often, you can also type `make uninstall' to remove the installed - files again. + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. Compilers and Options ===================== -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here @@ -88,25 +111,41 @@ is an example: Compiling For Multiple Architectures ==================================== -You can compile the package for more than one kind of computer at the + You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + Installation Names ================== -By default, `make install' installs the package's commands under + By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you @@ -117,16 +156,47 @@ Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to + Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The @@ -138,14 +208,58 @@ find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + Specifying the System Type ========================== -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: @@ -153,7 +267,8 @@ type, such as `sun4', or a canonical name which has the form: where SYSTEM can have one of these forms: - OS KERNEL-OS + OS + KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't @@ -171,9 +286,9 @@ eventually be run) with `--host=TYPE'. Sharing Defaults ================ -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. @@ -182,7 +297,7 @@ A warning: not all `configure' scripts look for a site script. Defining Variables ================== -Variables not defined in a site shell script can be set in the + Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set @@ -194,18 +309,27 @@ causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== -`configure' recognizes the following options to control how it operates. + `configure' recognizes the following options to control how it +operates. `--help' `-h' - Print a summary of the options to `configure', and exit. + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. `--version' `-V' @@ -232,6 +356,15 @@ an Autoconf bug. Until the bug is fixed you can use this workaround: Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. - diff --git a/configure.ac b/configure.ac index 7c3a0ed..d85c345 100644 --- a/configure.ac +++ b/configure.ac @@ -39,10 +39,7 @@ fi AC_ARG_WITH(pugl, [ --with-pugl Build with Pugl support]) if test x$with_pugl == xyes; then AC_MSG_WARN([*** Building with pugl support!]) - PUGL_LIBS="-lGL -lGLU -lX11" - PUGL_CFLAGS="" - AC_SUBST(PUGL_CFLAGS) - AC_SUBST(PUGL_LIBS) + CXXFLAGS="$CXXFLAGS -DPUGL -lGL -lGLU -lglut" fi AC_ARG_WITH(test, [ --with-test Build unit tests]) diff --git a/plugingui/Makefile.am b/plugingui/Makefile.am index 252db2a..9cf0218 100644 --- a/plugingui/Makefile.am +++ b/plugingui/Makefile.am @@ -41,6 +41,7 @@ EXTRA_DIST = \ listboxbasic.h \ listboxthin.h \ nativewindow.h \ + nativewindow.h \ nativewindow_win32.h \ nativewindow_x11.h \ painter.h \ diff --git a/plugingui/Makefile.am.plugingui b/plugingui/Makefile.am.plugingui index 121b128..f47582c 100644 --- a/plugingui/Makefile.am.plugingui +++ b/plugingui/Makefile.am.plugingui @@ -1,6 +1,8 @@ PLUGIN_GUI_SOURCES = \ $(top_srcdir)/hugin/hugin.c \ $(top_srcdir)/hugin/hugin_syslog.c \ + $(top_srcdir)/pugl/pugl/pugl_x11.c \ + $(top_srcdir)/plugingui/nativewindow_pugl.cc \ $(top_srcdir)/plugingui/nativewindow_x11.cc \ $(top_srcdir)/plugingui/nativewindow_win32.cc \ $(top_srcdir)/plugingui/plugingui.cc \ @@ -33,4 +35,4 @@ PLUGIN_GUI_SOURCES = \ PLUGIN_GUI_LIBS = $(X11_LIBS) $(PTHREAD_LIBS) $(PNG_LIBS) $(ZLIB_LIBS) -PLUGIN_GUI_CFLAGS = $(X11_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS) -I$(top_srcdir)/hugin -DWITH_HUG_SYSLOG -DWITH_HUG_MUTEX $(PTHREAD_CFLAGS) +PLUGIN_GUI_CFLAGS = $(X11_CFLAGS) $(PNG_CFLAGS) $(ZLIB_CFLAGS) -I$(top_srcdir)/hugin -DWITH_HUG_SYSLOG -DWITH_HUG_MUTEX $(PTHREAD_CFLAGS) -I$(top_srcdir)/pugl/pugl diff --git a/plugingui/window.cc b/plugingui/window.cc index 41d4d82..8337533 100644 --- a/plugingui/window.cc +++ b/plugingui/window.cc @@ -34,13 +34,16 @@ #include #include +#ifndef PUGL #ifdef X11 #include "nativewindow_x11.h" #endif/*X11*/ - #ifdef WIN32 #include "nativewindow_win32.h" #endif/*WIN32*/ +#else +#include "nativewindow_pugl.h" +#endif GUI::Window::Window() : Widget(NULL), wpixbuf(100, 100), back(":bg.png"), logo(":logo.png") @@ -55,13 +58,16 @@ GUI::Window::Window() _buttonDownFocus = NULL; _mouseFocus = NULL; +#ifndef PUGL #ifdef X11 native = new NativeWindowX11(this); #endif/*X11*/ - #ifdef WIN32 native = new NativeWindowWin32(this); #endif/*WIN32*/ +#else/*Use pugl*/ + native = new NativeWindowPugl(this); +#endif eventhandler = new GUI::EventHandler(native, this); } @@ -110,8 +116,8 @@ void GUI::Window::resize(int width, int height) resized(width, height); //#endif - native->resize(width, height); Widget::resize(width, height); + native->resize(width, height); } void GUI::Window::move(size_t x, size_t y) @@ -166,6 +172,7 @@ void GUI::Window::endPaint() void GUI::Window::updateBuffer() { + DEBUG(window, "Updating buffer\n"); memset(wpixbuf.buf, 0, wpixbuf.width * wpixbuf.height * 3); std::vector pl = getPixelBuffers(); diff --git a/pugl/.svn/entries b/pugl/.svn/entries new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/pugl/.svn/entries @@ -0,0 +1 @@ +12 diff --git a/pugl/.svn/format b/pugl/.svn/format new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/pugl/.svn/format @@ -0,0 +1 @@ +12 diff --git a/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base b/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base new file mode 100644 index 0000000..d3606cd --- /dev/null +++ b/pugl/.svn/pristine/02/0238ddf466c7ce520b9a9dbdca56fa01ac763fa1.svn-base @@ -0,0 +1,10 @@ +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: Pugl +Version: @PUGL_VERSION@ +Description: Lightweight portable OpenGL API +Libs: -L${libdir} -l@LIB_PUGL@ +Cflags: -I${includedir}/pugl-@PUGL_MAJOR_VERSION@ diff --git a/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base b/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base new file mode 100644 index 0000000..5f85d12 --- /dev/null +++ b/pugl/.svn/pristine/08/0824cfa41e889fddfea6ab4870872f0057e37373.svn-base @@ -0,0 +1,376 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_win.cpp Windows/WGL Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include + +#include "pugl_internal.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WHEEL_DELTA +# define WHEEL_DELTA 120 +#endif + +const int LOCAL_CLOSE_MSG = WM_USER + 50; + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; + WNDCLASS wc; +}; + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + // FIXME: This is nasty, and pugl should not have static anything. + // Should class be a parameter? Does this make sense on other platforms? + static int wc_count = 0; + char classNameBuf[256]; + _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); + + impl->wc.style = CS_OWNDC; + impl->wc.lpfnWndProc = wndProc; + impl->wc.cbClsExtra = 0; + impl->wc.cbWndExtra = 0; + impl->wc.hInstance = 0; + impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); + impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + impl->wc.lpszMenuName = NULL; + impl->wc.lpszClassName = classNameBuf; + RegisterClass(&impl->wc); + + int winFlags = WS_POPUPWINDOW | WS_CAPTION; + if (resizable) { + winFlags |= WS_SIZEBOX; + } + + // Adjust the overall window size to accomodate our requested client size + RECT wr = { 0, 0, width, height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + impl->hwnd = CreateWindowEx( + WS_EX_TOPMOST, + classNameBuf, title, + (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), + 0, 0, wr.right-wr.left, wr.bottom-wr.top, + (HWND)parent, NULL, NULL, NULL); + + if (!impl->hwnd) { + free(impl); + free(view); + return NULL; + } + + SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); + + impl->hdc = GetDC(impl->hwnd); + + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(impl->hdc, &pfd); + SetPixelFormat(impl->hdc, format, &pfd); + + impl->hglrc = wglCreateContext(impl->hdc); + wglMakeCurrent(impl->hdc, impl->hglrc); + + view->width = width; + view->height = height; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + wglMakeCurrent(NULL, NULL); + wglDeleteContext(view->impl->hglrc); + ReleaseDC(view->impl->hwnd, view->impl->hdc); + DestroyWindow(view->impl->hwnd); + UnregisterClass(view->impl->wc.lpszClassName, NULL); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +void +puglDisplay(PuglView* view) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + SwapBuffers(view->impl->hdc); + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(int sym) +{ + switch (sym) { + case VK_F1: return PUGL_KEY_F1; + case VK_F2: return PUGL_KEY_F2; + case VK_F3: return PUGL_KEY_F3; + case VK_F4: return PUGL_KEY_F4; + case VK_F5: return PUGL_KEY_F5; + case VK_F6: return PUGL_KEY_F6; + case VK_F7: return PUGL_KEY_F7; + case VK_F8: return PUGL_KEY_F8; + case VK_F9: return PUGL_KEY_F9; + case VK_F10: return PUGL_KEY_F10; + case VK_F11: return PUGL_KEY_F11; + case VK_F12: return PUGL_KEY_F12; + case VK_LEFT: return PUGL_KEY_LEFT; + case VK_UP: return PUGL_KEY_UP; + case VK_RIGHT: return PUGL_KEY_RIGHT; + case VK_DOWN: return PUGL_KEY_DOWN; + case VK_PRIOR: return PUGL_KEY_PAGE_UP; + case VK_NEXT: return PUGL_KEY_PAGE_DOWN; + case VK_HOME: return PUGL_KEY_HOME; + case VK_END: return PUGL_KEY_END; + case VK_INSERT: return PUGL_KEY_INSERT; + case VK_SHIFT: return PUGL_KEY_SHIFT; + case VK_CONTROL: return PUGL_KEY_CTRL; + case VK_MENU: return PUGL_KEY_ALT; + case VK_LWIN: return PUGL_KEY_SUPER; + case VK_RWIN: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) +{ + view->event_timestamp_ms = GetMessageTime(); + if (press) { + SetCapture(view->impl->hwnd); + } else { + ReleaseCapture(); + } + + if (view->mouseFunc) { + view->mouseFunc(view, button, press, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + } +} + +static void +setModifiers(PuglView* view) +{ + view->mods = 0; + view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; +} + +static LRESULT +handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + PuglKey key; + + setModifiers(view); + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + RECT rect; + GetClientRect(view->impl->hwnd, &rect); + puglReshape(view, rect.right, rect.bottom); + view->width = rect.right; + view->height = rect.bottom; + break; + case WM_PAINT: + BeginPaint(view->impl->hwnd, &ps); + puglDisplay(view); + EndPaint(view->impl->hwnd, &ps); + break; + case WM_MOUSEMOVE: + if (view->motionFunc) { + view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_LBUTTONDOWN: + processMouseEvent(view, 1, true, lParam); + break; + case WM_MBUTTONDOWN: + processMouseEvent(view, 2, true, lParam); + break; + case WM_RBUTTONDOWN: + processMouseEvent(view, 3, true, lParam); + break; + case WM_LBUTTONUP: + processMouseEvent(view, 1, false, lParam); + break; + case WM_MBUTTONUP: + processMouseEvent(view, 2, false, lParam); + break; + case WM_RBUTTONUP: + processMouseEvent(view, 3, false, lParam); + break; + case WM_MOUSEWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); + } + break; + case WM_MOUSEHWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0); + } + break; + case WM_KEYDOWN: + view->event_timestamp_ms = (GetMessageTime()); + if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { + break; + } // else nobreak + case WM_KEYUP: + if ((key = keySymToSpecial(wParam))) { + if (view->specialFunc) { + view->specialFunc(view, message == WM_KEYDOWN, key); + } + } else if (view->keyboardFunc) { + view->keyboardFunc(view, message == WM_KEYDOWN, wParam); + } + break; + case WM_QUIT: + case LOCAL_CLOSE_MSG: + if (view->closeFunc) { + view->closeFunc(view); + } + break; + default: + return DefWindowProc( + view->impl->hwnd, message, wParam, lParam); + } + + return 0; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + MSG msg; + while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { + handleMessage(view, msg.message, msg.wParam, msg.lParam); + } + + + if (view->redisplay) { + InvalidateRect(view->impl->hwnd, NULL, FALSE); + } + + return PUGL_SUCCESS; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); + switch (message) { + case WM_CREATE: + PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); + return 0; + case WM_CLOSE: + PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); + return 0; + case WM_DESTROY: + return 0; + default: + if (view) { + return handleMessage(view, message, wParam, lParam); + } else { + return DefWindowProc(hwnd, message, wParam, lParam); + } + } +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->hwnd; +} diff --git a/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base b/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base new file mode 100644 index 0000000..3682d14 --- /dev/null +++ b/pugl/.svn/pristine/09/0941e2033558edd90840bdb0cd9904dae31321b5.svn-base @@ -0,0 +1,155 @@ +#!/usr/bin/env python +import glob +import os +import sys + +from waflib.extras import autowaf as autowaf +import waflib.Logs as Logs, waflib.Options as Options + +# Version of this package (even if built as a child) +PUGL_VERSION = '0.2.0' +PUGL_MAJOR_VERSION = '0' + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +# Pugl uses the same version number for both library and package +PUGL_LIB_VERSION = PUGL_VERSION + +# Variables for 'waf dist' +APPNAME = 'pugl' +VERSION = PUGL_VERSION + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + if Options.platform == 'win32': + opt.load('compiler_cxx') + autowaf.set_options(opt) + opt.add_option('--test', action='store_true', default=False, dest='build_tests', + help="Build unit tests") + opt.add_option('--static', action='store_true', default=False, dest='static', + help="Build static library") + opt.add_option('--shared', action='store_true', default=False, dest='shared', + help="Build shared library") + +def configure(conf): + conf.load('compiler_c') + if Options.platform == 'win32': + conf.load('compiler_cxx') + autowaf.configure(conf) + autowaf.display_header('Pugl Configuration') + + if conf.env['MSVC_COMPILER']: + conf.env.append_unique('CFLAGS', ['-TP', '-MD']) + else: + conf.env.append_unique('CFLAGS', '-std=c99') + + # Shared library building is broken on win32 for some reason + conf.env['BUILD_TESTS'] = Options.options.build_tests + conf.env['BUILD_SHARED'] = (Options.platform != 'win32' or + Options.options.shared) + conf.env['BUILD_STATIC'] = (Options.options.build_tests or + Options.options.static) + + autowaf.define(conf, 'PUGL_VERSION', PUGL_VERSION) + conf.write_config_header('pugl_config.h', remove=False) + + conf.env['INCLUDES_PUGL'] = ['%s/pugl-%s' % (conf.env['INCLUDEDIR'], + PUGL_MAJOR_VERSION)] + conf.env['LIBPATH_PUGL'] = [conf.env['LIBDIR']] + conf.env['LIB_PUGL'] = ['pugl-%s' % PUGL_MAJOR_VERSION]; + + autowaf.display_msg(conf, "Static library", str(conf.env['BUILD_STATIC'])) + autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS'])) + print('') + +def build(bld): + # C Headers + includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION + bld.install_files(includedir, bld.path.ant_glob('pugl/*.h')) + + # Pkgconfig file + autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [], + {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION}) + + libflags = [ '-fvisibility=hidden' ] + framework = [] + libs = [] + if Options.platform == 'win32': + lang = 'cxx' + lib_source = ['pugl/pugl_win.cpp'] + libs = ['opengl32', 'glu32', 'gdi32', 'user32'] + defines = [] + elif Options.platform == 'darwin': + lang = 'c' # Objective C, actually + lib_source = ['pugl/pugl_osx.m'] + framework = ['Cocoa', 'OpenGL'] + defines = [] + else: + lang = 'c' + lib_source = ['pugl/pugl_x11.c'] + libs = ['X11', 'GL', 'GLU'] + defines = [] + if bld.env['MSVC_COMPILER']: + libflags = [] + + # Shared Library + if bld.env['BUILD_SHARED']: + obj = bld(features = '%s %sshlib' % (lang, lang), + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + framework = framework, + name = 'libpugl', + target = 'pugl-%s' % PUGL_MAJOR_VERSION, + vnum = PUGL_LIB_VERSION, + install_path = '${LIBDIR}', + defines = defines, + cflags = libflags + [ '-DPUGL_SHARED', + '-DPUGL_INTERNAL' ]) + + # Static library + if bld.env['BUILD_STATIC']: + obj = bld(features = '%s %sstlib' % (lang, lang), + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + framework = framework, + name = 'libpugl_static', + target = 'pugl-%s' % PUGL_MAJOR_VERSION, + vnum = PUGL_LIB_VERSION, + install_path = '${LIBDIR}', + defines = defines, + cflags = ['-DPUGL_INTERNAL']) + + if bld.env['BUILD_TESTS']: + test_libs = libs + test_cflags = [''] + + # Unit test program + obj = bld(features = 'c cprogram', + source = 'pugl_test.c', + includes = ['.', './src'], + use = 'libpugl_static', + lib = test_libs, + framework = framework, + target = 'pugl_test', + install_path = '', + defines = defines, + cflags = test_cflags) + +def lint(ctx): + subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* pugl/*', shell=True) + +from waflib import TaskGen +@TaskGen.extension('.m') +def m_hook(self, node): + "Alias .m files to be compiled the same as .c files, gcc will do the right thing." + return self.create_compiled_task('c', node) diff --git a/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base b/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base new file mode 100644 index 0000000..32ae229 Binary files /dev/null and b/pugl/.svn/pristine/4d/4d55d700ef68e3453f8f13725eb6d436815fee0b.svn-base differ diff --git a/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base b/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base new file mode 100644 index 0000000..c318d91 --- /dev/null +++ b/pugl/.svn/pristine/88/88b1206f77076c2b5398e8d26c6411f9d515fdb5.svn-base @@ -0,0 +1,400 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_osx.m OSX/Cocoa Pugl Implementation. +*/ + +#include + +#import + +#include "pugl_internal.h" + +@interface PuglWindow : NSWindow +{ +@public + PuglView* puglview; +} + +- (id) initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag; +- (void) setPuglview:(PuglView*)view; +- (BOOL) windowShouldClose:(id)sender; +@end + +@implementation PuglWindow + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag +{ + NSWindow* result = [super initWithContentRect:contentRect + styleMask:(NSClosableWindowMask | + NSTitledWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered defer:NO]; + + [result setAcceptsMouseMovedEvents:YES]; + [result setLevel: CGShieldingWindowLevel() + 1]; + + return result; +} + +- (void)setPuglview:(PuglView*)view +{ + puglview = view; + [self setContentSize:NSMakeSize(view->width, view->height) ]; +} + +- (BOOL)windowShouldClose:(id)sender +{ + if (puglview->closeFunc) + puglview->closeFunc(puglview); + return YES; +} + +@end + +void +puglDisplay(PuglView* view) +{ + if (view->displayFunc) { + view->displayFunc(view); + } +} + +@interface PuglOpenGLView : NSOpenGLView +{ + int colorBits; + int depthBits; +@public + PuglView* puglview; + + NSTrackingArea* trackingArea; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; +- (void) mouseMoved:(NSEvent*)event; +- (void) mouseDragged:(NSEvent*)event; +- (void) mouseDown:(NSEvent*)event; +- (void) mouseUp:(NSEvent*)event; +- (void) rightMouseDown:(NSEvent*)event; +- (void) rightMouseUp:(NSEvent*)event; +- (void) keyDown:(NSEvent*)event; +- (void) keyUp:(NSEvent*)event; +- (void) flagsChanged:(NSEvent*)event; + +@end + +@implementation PuglOpenGLView + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits +{ + colorBits = numColorBits; + depthBits = numDepthBits; + + NSOpenGLPixelFormatAttribute pixelAttribs[16] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, + colorBits, + NSOpenGLPFADepthSize, + depthBits, + 0 + }; + + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:pixelAttribs]; + + if (pixelFormat) { + self = [super initWithFrame:frame pixelFormat:pixelFormat]; + [pixelFormat release]; + if (self) { + [[self openGLContext] makeCurrentContext]; + [self reshape]; + } + } else { + self = nil; + } + + return self; +} + +- (void) reshape +{ + [[self openGLContext] update]; + + NSRect bounds = [self bounds]; + int width = bounds.size.width; + int height = bounds.size.height; + + if (puglview) { + /* NOTE: Apparently reshape gets called when the GC gets around to + deleting the view (?), so we must have reset puglview to NULL when + this comes around. + */ + if (puglview->reshapeFunc) { + puglview->reshapeFunc(puglview, width, height); + } else { + puglDefaultReshape(puglview, width, height); + } + + puglview->width = width; + puglview->height = height; + } +} + +- (void) drawRect:(NSRect)rect +{ + puglDisplay(puglview); + glFlush(); + glSwapAPPLE(); +} + +static unsigned +getModifiers(PuglView* view, NSEvent* ev) +{ + const unsigned modifierFlags = [ev modifierFlags]; + + view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); + + unsigned mods = 0; + mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; + mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; + mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; + mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; + return mods; +} + +-(void)updateTrackingAreas +{ + if (trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const int opts = (NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways); + trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; +} + +- (void)mouseEntered:(NSEvent*)theEvent +{ + [self updateTrackingAreas]; +} + +- (void)mouseExited:(NSEvent*)theEvent +{ +} + +- (void) mouseMoved:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDragged:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y); + } + [self updateTrackingAreas]; +} + +- (void) rightMouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y); + } +} + +- (void) rightMouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y); + } +} + +- (void) scrollWheel:(NSEvent*)event +{ + if (puglview->scrollFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->scrollFunc(puglview, + loc.x, puglview->height - loc.y, + [event deltaX], [event deltaY]); + } + [self updateTrackingAreas]; +} + +- (void) keyDown:(NSEvent*)event +{ + if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); + } +} + +- (void) keyUp:(NSEvent*)event +{ + if (puglview->keyboardFunc) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); + } +} + +- (void) flagsChanged:(NSEvent*)event +{ + if (puglview->specialFunc) { + const unsigned mods = getModifiers(puglview, [event modifierFlags]); + if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); + } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); + } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); + } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); + } + puglview->mods = mods; + } +} + +@end + +struct PuglInternalsImpl { + PuglOpenGLView* glview; + id window; +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + [NSAutoreleasePool new]; + [NSApplication sharedApplication]; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + id window = [[PuglWindow new]retain]; + + [window setPuglview:view]; + [window setTitle:titleString]; + + impl->glview = [PuglOpenGLView new]; + impl->window = window; + impl->glview->puglview = view; + + [window setContentView:impl->glview]; + [NSApp activateIgnoringOtherApps:YES]; + [window makeFirstResponder:impl->glview]; + + [window makeKeyAndOrderFront:window]; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + view->impl->glview->puglview = NULL; + [view->impl->window close]; + [view->impl->glview release]; + [view->impl->window release]; + free(view->impl); + free(view); +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + [view->impl->glview setNeedsDisplay: YES]; + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->glview; +} diff --git a/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base b/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base new file mode 100644 index 0000000..2a6a59f --- /dev/null +++ b/pugl/.svn/pristine/91/9136d3af8d5acc64a38a6f7a6c5385bb371da642.svn-base @@ -0,0 +1,357 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl.h API for Pugl, a minimal portable API for OpenGL. +*/ + +#ifndef PUGL_H_INCLUDED +#define PUGL_H_INCLUDED + +#include + +/* + This API is pure portable C and contains no platform specific elements, or + even a GL dependency. However, unfortunately GL includes vary across + platforms so they are included here to allow for pure portable programs. +*/ +#ifdef __APPLE__ +# include "OpenGL/gl.h" +#else +# ifdef _WIN32 +# include /* Broken Windows GL headers require this */ +# endif +# include "GL/gl.h" +#endif + +#ifdef PUGL_SHARED +# ifdef _WIN32 +# define PUGL_LIB_IMPORT __declspec(dllimport) +# define PUGL_LIB_EXPORT __declspec(dllexport) +# else +# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) +# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef PUGL_INTERNAL +# define PUGL_API PUGL_LIB_EXPORT +# else +# define PUGL_API PUGL_LIB_IMPORT +# endif +#else +# define PUGL_API +#endif + +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + +/** + @defgroup pugl Pugl + A minimal portable API for OpenGL. + @{ +*/ + +/** + An OpenGL view. +*/ +typedef struct PuglViewImpl PuglView; + +/** + A native window handle. + + On X11, this is a Window. + On OSX, this is an NSView*. + On Windows, this is a HWND. +*/ +typedef intptr_t PuglNativeWindow; + +/** + Return status code. +*/ +typedef enum { + PUGL_SUCCESS = 0 +} PuglStatus; + +/** + Convenience symbols for ASCII control characters. +*/ +typedef enum { + PUGL_CHAR_BACKSPACE = 0x08, + PUGL_CHAR_ESCAPE = 0x1B, + PUGL_CHAR_DELETE = 0x7F +} PuglChar; + +/** + Special (non-Unicode) keyboard keys. +*/ +typedef enum { + PUGL_KEY_F1 = 1, + PUGL_KEY_F2, + PUGL_KEY_F3, + PUGL_KEY_F4, + PUGL_KEY_F5, + PUGL_KEY_F6, + PUGL_KEY_F7, + PUGL_KEY_F8, + PUGL_KEY_F9, + PUGL_KEY_F10, + PUGL_KEY_F11, + PUGL_KEY_F12, + PUGL_KEY_LEFT, + PUGL_KEY_UP, + PUGL_KEY_RIGHT, + PUGL_KEY_DOWN, + PUGL_KEY_PAGE_UP, + PUGL_KEY_PAGE_DOWN, + PUGL_KEY_HOME, + PUGL_KEY_END, + PUGL_KEY_INSERT, + PUGL_KEY_SHIFT, + PUGL_KEY_CTRL, + PUGL_KEY_ALT, + PUGL_KEY_SUPER +} PuglKey; + +/** + Keyboard modifier flags. +*/ +typedef enum { + PUGL_MOD_SHIFT = 1, /**< Shift key */ + PUGL_MOD_CTRL = 1 << 1, /**< Control key */ + PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ + PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ +} PuglMod; + +/** + Handle for opaque user data. +*/ +typedef void* PuglHandle; + +/** + A function called when the window is closed. +*/ +typedef void (*PuglCloseFunc)(PuglView* view); + +/** + A function called to draw the view contents with OpenGL. +*/ +typedef void (*PuglDisplayFunc)(PuglView* view); + +/** + A function called when a key is pressed or released. + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key Unicode point of the key pressed. +*/ +typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); + +/** + A function called when the pointer moves. + @param view The view the event occured in. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); + +/** + A function called when a mouse button is pressed or released. + @param view The view the event occured in. + @param button The button number (1 = left, 2 = middle, 3 = right). + @param press True if the key was pressed, false if released. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMouseFunc)( + PuglView* view, int button, bool press, int x, int y); + +/** + A function called when the view is resized. + @param view The view being resized. + @param width The new view width. + @param height The new view height. +*/ +typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); + +/** + A function called on scrolling (e.g. mouse wheel or track pad). + + The distances used here are in "lines", a single tick of a clicking mouse + wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and + devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + + @param view The view being scrolled. + @param dx The scroll x distance. + @param dx The scroll y distance. +*/ +typedef void (*PuglScrollFunc)(PuglView* view, + int x, + int y, + float dx, + float dy); + +/** + A function called when a special key is pressed or released. + + This callback allows the use of keys that do not have unicode points. + + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key The key pressed. +*/ +typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); + +/** + Create a new GL window. + @param parent Parent window, or 0 for top level. + @param title Window title, or NULL. + @param width Window width in pixels. + @param height Window height in pixels. + @param resizable Whether window should be user resizable. + @param visible Whether window should be initially visible. +*/ +PUGL_API PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible); + +/** + Set the handle to be passed to all callbacks. + + This is generally a pointer to a struct which contains all necessary state. + Everything needed in callbacks should be here, not in static variables. + + Note the lack of this facility makes GLUT unsuitable for plugins or + non-trivial programs; this mistake is largely why Pugl exists. +*/ +PUGL_API void +puglSetHandle(PuglView* view, PuglHandle handle); + +/** + Get the handle to be passed to all callbacks. +*/ +PUGL_API PuglHandle +puglGetHandle(PuglView* view); + +/** + Return the timestamp (if any) of the currently-processing event. +*/ +PUGL_API uint32_t +puglGetEventTimestamp(PuglView* view); + +/** + Get the currently active modifiers (PuglMod flags). + + This should only be called from an event handler. +*/ +PUGL_API int +puglGetModifiers(PuglView* view); + +/** + Ignore synthetic repeated key events. +*/ +PUGL_API void +puglIgnoreKeyRepeat(PuglView* view, bool ignore); + +/** + Set the function to call when the window is closed. +*/ +PUGL_API void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); + +/** + Set the display function which should draw the UI using GL. +*/ +PUGL_API void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); + +/** + Set the function to call on keyboard events. +*/ +PUGL_API void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); + +/** + Set the function to call on mouse motion. +*/ +PUGL_API void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); + +/** + Set the function to call on mouse button events. +*/ +PUGL_API void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); + +/** + Set the function to call on scroll events. +*/ +PUGL_API void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); + +/** + Set the function to call on special events. +*/ +PUGL_API void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); + +/** + Set the function to call when the window size changes. +*/ +PUGL_API void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); + +/** + Return the native window handle. +*/ +PUGL_API PuglNativeWindow +puglGetNativeWindow(PuglView* view); + +/** + Process all pending window events. + + This handles input events as well as rendering, so it should be called + regularly and rapidly enough to keep the UI responsive. +*/ +PUGL_API PuglStatus +puglProcessEvents(PuglView* view); + +/** + Request a redisplay on the next call to puglProcessEvents(). +*/ +PUGL_API void +puglPostRedisplay(PuglView* view); + +/** + Destroy a GL window. +*/ +PUGL_API void +puglDestroy(PuglView* view); + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PUGL_H_INCLUDED */ diff --git a/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base b/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base new file mode 100644 index 0000000..202ec66 --- /dev/null +++ b/pugl/.svn/pristine/95/95e5f0088ae9801b2069c0f247160a6c44836112.svn-base @@ -0,0 +1,5 @@ +Author: + David Robillard + +Original GLX inspiration, portability fixes: + Ben Loftis diff --git a/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base b/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base new file mode 100644 index 0000000..8cdada8 --- /dev/null +++ b/pugl/.svn/pristine/9a/9aa2a332bc430fd6f006d218654060baa9af14bb.svn-base @@ -0,0 +1,143 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_internal.h Private platform-independent definitions. + + Note this file contains function definitions, so it must be compiled into + the final binary exactly once. Each platform specific implementation file + including it once should achieve this. +*/ + +#include "pugl.h" + +typedef struct PuglInternalsImpl PuglInternals; + +struct PuglViewImpl { + PuglHandle handle; + PuglCloseFunc closeFunc; + PuglDisplayFunc displayFunc; + PuglKeyboardFunc keyboardFunc; + PuglMotionFunc motionFunc; + PuglMouseFunc mouseFunc; + PuglReshapeFunc reshapeFunc; + PuglScrollFunc scrollFunc; + PuglSpecialFunc specialFunc; + + PuglInternals* impl; + + int width; + int height; + int mods; + bool mouse_in_view; + bool ignoreKeyRepeat; + bool redisplay; + uint32_t event_timestamp_ms; +}; + +void +puglSetHandle(PuglView* view, PuglHandle handle) +{ + view->handle = handle; +} + +PuglHandle +puglGetHandle(PuglView* view) +{ + return view->handle; +} + +uint32_t +puglGetEventTimestamp(PuglView* view) +{ + return view->event_timestamp_ms; +} + +int +puglGetModifiers(PuglView* view) +{ + return view->mods; +} + +void +puglDefaultReshape(PuglView* view, int width, int height) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, 0, 1); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + return; + + // unused + (void)view; +} + +void +puglIgnoreKeyRepeat(PuglView* view, bool ignore) +{ + view->ignoreKeyRepeat = ignore; +} + +void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) +{ + view->closeFunc = closeFunc; +} + +void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) +{ + view->displayFunc = displayFunc; +} + +void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) +{ + view->keyboardFunc = keyboardFunc; +} + +void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) +{ + view->motionFunc = motionFunc; +} + +void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) +{ + view->mouseFunc = mouseFunc; +} + +void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) +{ + view->reshapeFunc = reshapeFunc; +} + +void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) +{ + view->scrollFunc = scrollFunc; +} + +void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) +{ + view->specialFunc = specialFunc; +} diff --git a/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base b/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base new file mode 100644 index 0000000..c25b273 --- /dev/null +++ b/pugl/.svn/pristine/ae/ae22c035ba53b96a90015d3789aac71b53267aa5.svn-base @@ -0,0 +1,386 @@ +/* + Copyright 2012-2014 David Robillard + Copyright 2011-2012 Ben Loftis, Harrison Consoles + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_x11.c X11 Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pugl_internal.h" + +struct PuglInternalsImpl { + Display* display; + int screen; + Window win; + GLXContext ctx; + Bool doubleBuffered; +}; + +/** + Attributes for single-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListSgl[] = { + GLX_RGBA, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +/** + Attributes for double-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListDbl[] = { + GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + impl->display = XOpenDisplay(0); + impl->screen = DefaultScreen(impl->display); + + XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); + if (!vi) { + vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); + impl->doubleBuffered = False; + printf("singlebuffered rendering will be used, no doublebuffering available\n"); + } else { + impl->doubleBuffered = True; + printf("doublebuffered rendering available\n"); + } + + int glxMajor, glxMinor; + glXQueryVersion(impl->display, &glxMajor, &glxMinor); + printf("GLX-Version %d.%d\n", glxMajor, glxMinor); + + impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); + + Window xParent = parent + ? (Window)parent + : RootWindow(impl->display, impl->screen); + + Colormap cmap = XCreateColormap( + impl->display, xParent, vi->visual, AllocNone); + + XSetWindowAttributes attr; + memset(&attr, 0, sizeof(XSetWindowAttributes)); + attr.colormap = cmap; + attr.border_pixel = 0; + + attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask + | PointerMotionMask | StructureNotifyMask; + + impl->win = XCreateWindow( + impl->display, xParent, + 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &attr); + + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + if (!resizable) { + sizeHints.flags = PMinSize|PMaxSize; + sizeHints.min_width = width; + sizeHints.min_height = height; + sizeHints.max_width = width; + sizeHints.max_height = height; + XSetNormalHints(impl->display, impl->win, &sizeHints); + } + + if (title) { + XStoreName(impl->display, impl->win, title); + } + + if (!parent) { + Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); + } + + if (visible) { + XMapRaised(impl->display, impl->win); + } + + if (glXIsDirect(impl->display, impl->ctx)) { + printf("DRI enabled\n"); + } else { + printf("No DRI available\n"); + } + + XFree(vi); + + return view; +} + +void +puglDestroy(PuglView* view) +{ + if (!view) { + return; + } + + glXDestroyContext(view->impl->display, view->impl->ctx); + XDestroyWindow(view->impl->display, view->impl->win); + XCloseDisplay(view->impl->display); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +static void +puglDisplay(PuglView* view) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + if (view->impl->doubleBuffered) { + glXSwapBuffers(view->impl->display, view->impl->win); + } + + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(KeySym sym) +{ + switch (sym) { + case XK_F1: return PUGL_KEY_F1; + case XK_F2: return PUGL_KEY_F2; + case XK_F3: return PUGL_KEY_F3; + case XK_F4: return PUGL_KEY_F4; + case XK_F5: return PUGL_KEY_F5; + case XK_F6: return PUGL_KEY_F6; + case XK_F7: return PUGL_KEY_F7; + case XK_F8: return PUGL_KEY_F8; + case XK_F9: return PUGL_KEY_F9; + case XK_F10: return PUGL_KEY_F10; + case XK_F11: return PUGL_KEY_F11; + case XK_F12: return PUGL_KEY_F12; + case XK_Left: return PUGL_KEY_LEFT; + case XK_Up: return PUGL_KEY_UP; + case XK_Right: return PUGL_KEY_RIGHT; + case XK_Down: return PUGL_KEY_DOWN; + case XK_Page_Up: return PUGL_KEY_PAGE_UP; + case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; + case XK_Home: return PUGL_KEY_HOME; + case XK_End: return PUGL_KEY_END; + case XK_Insert: return PUGL_KEY_INSERT; + case XK_Shift_L: return PUGL_KEY_SHIFT; + case XK_Shift_R: return PUGL_KEY_SHIFT; + case XK_Control_L: return PUGL_KEY_CTRL; + case XK_Control_R: return PUGL_KEY_CTRL; + case XK_Alt_L: return PUGL_KEY_ALT; + case XK_Alt_R: return PUGL_KEY_ALT; + case XK_Super_L: return PUGL_KEY_SUPER; + case XK_Super_R: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +setModifiers(PuglView* view, unsigned xstate, unsigned xtime) +{ + view->event_timestamp_ms = xtime; + + view->mods = 0; + view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; + view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; + view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; + view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; +} + +static void +dispatchKey(PuglView* view, XEvent* event, bool press) +{ + KeySym sym; + char str[5]; + const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); + if (n == 0) { + return; + } else if (n > 1) { + fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); + return; + } + + const PuglKey special = keySymToSpecial(sym); + if (special && view->specialFunc) { + view->specialFunc(view, press, special); + } else if (!special && view->keyboardFunc) { + view->keyboardFunc(view, press, str[0]); + } +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + XEvent event; + while (XPending(view->impl->display) > 0) { + XNextEvent(view->impl->display, &event); + switch (event.type) { + case MapNotify: + puglReshape(view, view->width, view->height); + break; + case ConfigureNotify: + if ((event.xconfigure.width != view->width) || + (event.xconfigure.height != view->height)) { + puglReshape(view, + event.xconfigure.width, + event.xconfigure.height); + } + break; + case Expose: + if (event.xexpose.count != 0) { + break; + } + puglDisplay(view); + break; + case MotionNotify: + setModifiers(view, event.xmotion.state, event.xmotion.time); + if (view->motionFunc) { + view->motionFunc(view, event.xmotion.x, event.xmotion.y); + } + break; + case ButtonPress: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { + if (view->scrollFunc) { + float dx = 0, dy = 0; + switch (event.xbutton.button) { + case 4: dy = 1.0f; break; + case 5: dy = -1.0f; break; + case 6: dx = -1.0f; break; + case 7: dx = 1.0f; break; + } + view->scrollFunc(view, + event.xbutton.x, event.xbutton.y, + dx, dy); + } + break; + } + // nobreak + case ButtonRelease: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (view->mouseFunc && + (event.xbutton.button < 4 || event.xbutton.button > 7)) { + view->mouseFunc(view, + event.xbutton.button, event.type == ButtonPress, + event.xbutton.x, event.xbutton.y); + } + break; + case KeyPress: + setModifiers(view, event.xkey.state, event.xkey.time); + dispatchKey(view, &event, true); + break; + case KeyRelease: + setModifiers(view, event.xkey.state, event.xkey.time); + if (view->ignoreKeyRepeat && + XEventsQueued(view->impl->display, QueuedAfterReading)) { + XEvent next; + XPeekEvent(view->impl->display, &next); + if (next.type == KeyPress && + next.xkey.time == event.xkey.time && + next.xkey.keycode == event.xkey.keycode) { + XNextEvent(view->impl->display, &event); + break; + } + } + dispatchKey(view, &event, false); + break; + case ClientMessage: + if (!strcmp(XGetAtomName(view->impl->display, + event.xclient.message_type), + "WM_PROTOCOLS")) { + if (view->closeFunc) { + view->closeFunc(view); + } + } + break; + default: + break; + } + } + + if (view->redisplay) { + puglDisplay(view); + } + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return view->impl->win; +} diff --git a/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base b/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base new file mode 100644 index 0000000..c3c2af6 --- /dev/null +++ b/pugl/.svn/pristine/c0/c0dfec121f410a9df9b6c7482d3a1cffb1060b67.svn-base @@ -0,0 +1,13 @@ +Copyright 2011-2012 David Robillard + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base b/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base new file mode 100644 index 0000000..b9a54f5 --- /dev/null +++ b/pugl/.svn/pristine/ca/caaef1caf9335e555e9f2cb4515b6ba67f379f12.svn-base @@ -0,0 +1,193 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_test.c A simple Pugl test that creates a top-level window. +*/ + +#include +#include + +#include "pugl/pugl.h" + +// Argh! +#ifdef __APPLE__ +# include +#else +# include +#endif + +static int quit = 0; +static float xAngle = 0.0f; +static float yAngle = 0.0f; +static float dist = 10.0f; + +static void +onReshape(PuglView* view, int width, int height) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, width, height); + gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +static void +onDisplay(PuglView* view) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + glTranslatef(0.0f, 0.0f, dist * -1); + glRotatef(xAngle, 0.0f, 1.0f, 0.0f); + glRotatef(yAngle, 1.0f, 0.0f, 0.0f); + + glBegin(GL_QUADS); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + + glEnd(); +} + +static void +printModifiers(PuglView* view) +{ + int mods = puglGetModifiers(view); + fprintf(stderr, "Modifiers:%s%s%s%s\n", + (mods & PUGL_MOD_SHIFT) ? " Shift" : "", + (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", + (mods & PUGL_MOD_ALT) ? " Alt" : "", + (mods & PUGL_MOD_SUPER) ? " Super" : ""); +} + +static void +onKeyboard(PuglView* view, bool press, uint32_t key) +{ + fprintf(stderr, "Key %c %s ", (char)key, press ? "down" : "up"); + printModifiers(view); + if (key == 'q' || key == 'Q' || key == PUGL_CHAR_ESCAPE || + key == PUGL_CHAR_DELETE || key == PUGL_CHAR_BACKSPACE) { + quit = 1; + } +} + +static void +onSpecial(PuglView* view, bool press, PuglKey key) +{ + fprintf(stderr, "Special key %d %s ", key, press ? "down" : "up"); + printModifiers(view); +} + +static void +onMotion(PuglView* view, int x, int y) +{ + xAngle = x % 360; + yAngle = y % 360; + puglPostRedisplay(view); +} + +static void +onMouse(PuglView* view, int button, bool press, int x, int y) +{ + fprintf(stderr, "Mouse %d %s at %d,%d ", + button, press ? "down" : "up", x, y); + printModifiers(view); +} + +static void +onScroll(PuglView* view, int x, int y, float dx, float dy) +{ + fprintf(stderr, "Scroll %d %d %f %f ", x, y, dx, dy); + printModifiers(view); + dist += dy / 4.0f; + puglPostRedisplay(view); +} + +static void +onClose(PuglView* view) +{ + quit = 1; +} + +int +main(int argc, char** argv) +{ + bool ignoreKeyRepeat = false; + bool resizable = false; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-h")) { + printf("USAGE: %s [OPTIONS]...\n\n" + " -h Display this help\n" + " -i Ignore key repeat\n" + " -r Resizable window\n", argv[0]); + return 0; + } else if (!strcmp(argv[i], "-i")) { + ignoreKeyRepeat = true; + } else if (!strcmp(argv[i], "-r")) { + resizable = true; + } else { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + } + } + + PuglView* view = puglCreate(0, "Pugl Test", 512, 512, resizable, true); + puglIgnoreKeyRepeat(view, ignoreKeyRepeat); + puglSetKeyboardFunc(view, onKeyboard); + puglSetMotionFunc(view, onMotion); + puglSetMouseFunc(view, onMouse); + puglSetScrollFunc(view, onScroll); + puglSetSpecialFunc(view, onSpecial); + puglSetDisplayFunc(view, onDisplay); + puglSetReshapeFunc(view, onReshape); + puglSetCloseFunc(view, onClose); + + while (!quit) { + puglProcessEvents(view); + } + + puglDestroy(view); + return 0; +} diff --git a/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base b/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base new file mode 100644 index 0000000..87742f4 --- /dev/null +++ b/pugl/.svn/pristine/e3/e39501dc0e0ae55c38b63429be01f2a045d76823.svn-base @@ -0,0 +1,27 @@ +PUGL +==== + +Pugl is a minimal portable API for OpenGL GUIs which supports embedding and is +suitable for use in plugins. It works on X11, Mac OS X, and Windows. + +Pugl is vaguely similar to GLUT, but with some significant distinctions: + + * Minimal in scope, providing only what is necessary to draw and receive + keyboard and mouse input. + + * No reliance on static data whatsoever, so the API can be used in plugins or + multiple independent parts of a program. + + * Single implementation, which is small, liberally licensed Free / Open Source + Software, and suitable for direct inclusion in programs if avoiding a + library dependency is desired. + + * Support for embedding in other windows, so Pugl code can draw to a widget + inside a larger GUI. + + * More complete support for keyboard input, including additional "special" + keys, modifiers, and support for detecting individual modifier key presses. + +For more information, see . + + -- David Robillard diff --git a/pugl/.svn/wc.db b/pugl/.svn/wc.db new file mode 100644 index 0000000..ca0c8da Binary files /dev/null and b/pugl/.svn/wc.db differ diff --git a/pugl/AUTHORS b/pugl/AUTHORS new file mode 100644 index 0000000..202ec66 --- /dev/null +++ b/pugl/AUTHORS @@ -0,0 +1,5 @@ +Author: + David Robillard + +Original GLX inspiration, portability fixes: + Ben Loftis diff --git a/pugl/COPYING b/pugl/COPYING new file mode 100644 index 0000000..c3c2af6 --- /dev/null +++ b/pugl/COPYING @@ -0,0 +1,13 @@ +Copyright 2011-2012 David Robillard + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/pugl/README b/pugl/README new file mode 100644 index 0000000..87742f4 --- /dev/null +++ b/pugl/README @@ -0,0 +1,27 @@ +PUGL +==== + +Pugl is a minimal portable API for OpenGL GUIs which supports embedding and is +suitable for use in plugins. It works on X11, Mac OS X, and Windows. + +Pugl is vaguely similar to GLUT, but with some significant distinctions: + + * Minimal in scope, providing only what is necessary to draw and receive + keyboard and mouse input. + + * No reliance on static data whatsoever, so the API can be used in plugins or + multiple independent parts of a program. + + * Single implementation, which is small, liberally licensed Free / Open Source + Software, and suitable for direct inclusion in programs if avoiding a + library dependency is desired. + + * Support for embedding in other windows, so Pugl code can draw to a widget + inside a larger GUI. + + * More complete support for keyboard input, including additional "special" + keys, modifiers, and support for detecting individual modifier key presses. + +For more information, see . + + -- David Robillard diff --git a/pugl/pugl.pc.in b/pugl/pugl.pc.in new file mode 100644 index 0000000..d3606cd --- /dev/null +++ b/pugl/pugl.pc.in @@ -0,0 +1,10 @@ +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: Pugl +Version: @PUGL_VERSION@ +Description: Lightweight portable OpenGL API +Libs: -L${libdir} -l@LIB_PUGL@ +Cflags: -I${includedir}/pugl-@PUGL_MAJOR_VERSION@ diff --git a/pugl/pugl/pugl.h b/pugl/pugl/pugl.h new file mode 100644 index 0000000..2a6a59f --- /dev/null +++ b/pugl/pugl/pugl.h @@ -0,0 +1,357 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl.h API for Pugl, a minimal portable API for OpenGL. +*/ + +#ifndef PUGL_H_INCLUDED +#define PUGL_H_INCLUDED + +#include + +/* + This API is pure portable C and contains no platform specific elements, or + even a GL dependency. However, unfortunately GL includes vary across + platforms so they are included here to allow for pure portable programs. +*/ +#ifdef __APPLE__ +# include "OpenGL/gl.h" +#else +# ifdef _WIN32 +# include /* Broken Windows GL headers require this */ +# endif +# include "GL/gl.h" +#endif + +#ifdef PUGL_SHARED +# ifdef _WIN32 +# define PUGL_LIB_IMPORT __declspec(dllimport) +# define PUGL_LIB_EXPORT __declspec(dllexport) +# else +# define PUGL_LIB_IMPORT __attribute__((visibility("default"))) +# define PUGL_LIB_EXPORT __attribute__((visibility("default"))) +# endif +# ifdef PUGL_INTERNAL +# define PUGL_API PUGL_LIB_EXPORT +# else +# define PUGL_API PUGL_LIB_IMPORT +# endif +#else +# define PUGL_API +#endif + +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + +/** + @defgroup pugl Pugl + A minimal portable API for OpenGL. + @{ +*/ + +/** + An OpenGL view. +*/ +typedef struct PuglViewImpl PuglView; + +/** + A native window handle. + + On X11, this is a Window. + On OSX, this is an NSView*. + On Windows, this is a HWND. +*/ +typedef intptr_t PuglNativeWindow; + +/** + Return status code. +*/ +typedef enum { + PUGL_SUCCESS = 0 +} PuglStatus; + +/** + Convenience symbols for ASCII control characters. +*/ +typedef enum { + PUGL_CHAR_BACKSPACE = 0x08, + PUGL_CHAR_ESCAPE = 0x1B, + PUGL_CHAR_DELETE = 0x7F +} PuglChar; + +/** + Special (non-Unicode) keyboard keys. +*/ +typedef enum { + PUGL_KEY_F1 = 1, + PUGL_KEY_F2, + PUGL_KEY_F3, + PUGL_KEY_F4, + PUGL_KEY_F5, + PUGL_KEY_F6, + PUGL_KEY_F7, + PUGL_KEY_F8, + PUGL_KEY_F9, + PUGL_KEY_F10, + PUGL_KEY_F11, + PUGL_KEY_F12, + PUGL_KEY_LEFT, + PUGL_KEY_UP, + PUGL_KEY_RIGHT, + PUGL_KEY_DOWN, + PUGL_KEY_PAGE_UP, + PUGL_KEY_PAGE_DOWN, + PUGL_KEY_HOME, + PUGL_KEY_END, + PUGL_KEY_INSERT, + PUGL_KEY_SHIFT, + PUGL_KEY_CTRL, + PUGL_KEY_ALT, + PUGL_KEY_SUPER +} PuglKey; + +/** + Keyboard modifier flags. +*/ +typedef enum { + PUGL_MOD_SHIFT = 1, /**< Shift key */ + PUGL_MOD_CTRL = 1 << 1, /**< Control key */ + PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ + PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ +} PuglMod; + +/** + Handle for opaque user data. +*/ +typedef void* PuglHandle; + +/** + A function called when the window is closed. +*/ +typedef void (*PuglCloseFunc)(PuglView* view); + +/** + A function called to draw the view contents with OpenGL. +*/ +typedef void (*PuglDisplayFunc)(PuglView* view); + +/** + A function called when a key is pressed or released. + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key Unicode point of the key pressed. +*/ +typedef void (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); + +/** + A function called when the pointer moves. + @param view The view the event occured in. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); + +/** + A function called when a mouse button is pressed or released. + @param view The view the event occured in. + @param button The button number (1 = left, 2 = middle, 3 = right). + @param press True if the key was pressed, false if released. + @param x The window-relative x coordinate of the pointer. + @param y The window-relative y coordinate of the pointer. +*/ +typedef void (*PuglMouseFunc)( + PuglView* view, int button, bool press, int x, int y); + +/** + A function called when the view is resized. + @param view The view being resized. + @param width The new view width. + @param height The new view height. +*/ +typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); + +/** + A function called on scrolling (e.g. mouse wheel or track pad). + + The distances used here are in "lines", a single tick of a clicking mouse + wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and + devices support finer resolution and/or higher values for fast scrolls, + so programs should handle any value gracefully. + + @param view The view being scrolled. + @param dx The scroll x distance. + @param dx The scroll y distance. +*/ +typedef void (*PuglScrollFunc)(PuglView* view, + int x, + int y, + float dx, + float dy); + +/** + A function called when a special key is pressed or released. + + This callback allows the use of keys that do not have unicode points. + + @param view The view the event occured in. + @param press True if the key was pressed, false if released. + @param key The key pressed. +*/ +typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); + +/** + Create a new GL window. + @param parent Parent window, or 0 for top level. + @param title Window title, or NULL. + @param width Window width in pixels. + @param height Window height in pixels. + @param resizable Whether window should be user resizable. + @param visible Whether window should be initially visible. +*/ +PUGL_API PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible); + +/** + Set the handle to be passed to all callbacks. + + This is generally a pointer to a struct which contains all necessary state. + Everything needed in callbacks should be here, not in static variables. + + Note the lack of this facility makes GLUT unsuitable for plugins or + non-trivial programs; this mistake is largely why Pugl exists. +*/ +PUGL_API void +puglSetHandle(PuglView* view, PuglHandle handle); + +/** + Get the handle to be passed to all callbacks. +*/ +PUGL_API PuglHandle +puglGetHandle(PuglView* view); + +/** + Return the timestamp (if any) of the currently-processing event. +*/ +PUGL_API uint32_t +puglGetEventTimestamp(PuglView* view); + +/** + Get the currently active modifiers (PuglMod flags). + + This should only be called from an event handler. +*/ +PUGL_API int +puglGetModifiers(PuglView* view); + +/** + Ignore synthetic repeated key events. +*/ +PUGL_API void +puglIgnoreKeyRepeat(PuglView* view, bool ignore); + +/** + Set the function to call when the window is closed. +*/ +PUGL_API void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); + +/** + Set the display function which should draw the UI using GL. +*/ +PUGL_API void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); + +/** + Set the function to call on keyboard events. +*/ +PUGL_API void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); + +/** + Set the function to call on mouse motion. +*/ +PUGL_API void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); + +/** + Set the function to call on mouse button events. +*/ +PUGL_API void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); + +/** + Set the function to call on scroll events. +*/ +PUGL_API void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); + +/** + Set the function to call on special events. +*/ +PUGL_API void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); + +/** + Set the function to call when the window size changes. +*/ +PUGL_API void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); + +/** + Return the native window handle. +*/ +PUGL_API PuglNativeWindow +puglGetNativeWindow(PuglView* view); + +/** + Process all pending window events. + + This handles input events as well as rendering, so it should be called + regularly and rapidly enough to keep the UI responsive. +*/ +PUGL_API PuglStatus +puglProcessEvents(PuglView* view); + +/** + Request a redisplay on the next call to puglProcessEvents(). +*/ +PUGL_API void +puglPostRedisplay(PuglView* view); + +/** + Destroy a GL window. +*/ +PUGL_API void +puglDestroy(PuglView* view); + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PUGL_H_INCLUDED */ diff --git a/pugl/pugl/pugl_internal.h b/pugl/pugl/pugl_internal.h new file mode 100644 index 0000000..8cdada8 --- /dev/null +++ b/pugl/pugl/pugl_internal.h @@ -0,0 +1,143 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_internal.h Private platform-independent definitions. + + Note this file contains function definitions, so it must be compiled into + the final binary exactly once. Each platform specific implementation file + including it once should achieve this. +*/ + +#include "pugl.h" + +typedef struct PuglInternalsImpl PuglInternals; + +struct PuglViewImpl { + PuglHandle handle; + PuglCloseFunc closeFunc; + PuglDisplayFunc displayFunc; + PuglKeyboardFunc keyboardFunc; + PuglMotionFunc motionFunc; + PuglMouseFunc mouseFunc; + PuglReshapeFunc reshapeFunc; + PuglScrollFunc scrollFunc; + PuglSpecialFunc specialFunc; + + PuglInternals* impl; + + int width; + int height; + int mods; + bool mouse_in_view; + bool ignoreKeyRepeat; + bool redisplay; + uint32_t event_timestamp_ms; +}; + +void +puglSetHandle(PuglView* view, PuglHandle handle) +{ + view->handle = handle; +} + +PuglHandle +puglGetHandle(PuglView* view) +{ + return view->handle; +} + +uint32_t +puglGetEventTimestamp(PuglView* view) +{ + return view->event_timestamp_ms; +} + +int +puglGetModifiers(PuglView* view) +{ + return view->mods; +} + +void +puglDefaultReshape(PuglView* view, int width, int height) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, 0, 1); + glViewport(0, 0, width, height); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + return; + + // unused + (void)view; +} + +void +puglIgnoreKeyRepeat(PuglView* view, bool ignore) +{ + view->ignoreKeyRepeat = ignore; +} + +void +puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) +{ + view->closeFunc = closeFunc; +} + +void +puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) +{ + view->displayFunc = displayFunc; +} + +void +puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) +{ + view->keyboardFunc = keyboardFunc; +} + +void +puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) +{ + view->motionFunc = motionFunc; +} + +void +puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) +{ + view->mouseFunc = mouseFunc; +} + +void +puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) +{ + view->reshapeFunc = reshapeFunc; +} + +void +puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) +{ + view->scrollFunc = scrollFunc; +} + +void +puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) +{ + view->specialFunc = specialFunc; +} diff --git a/pugl/pugl/pugl_osx.m b/pugl/pugl/pugl_osx.m new file mode 100644 index 0000000..c318d91 --- /dev/null +++ b/pugl/pugl/pugl_osx.m @@ -0,0 +1,400 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_osx.m OSX/Cocoa Pugl Implementation. +*/ + +#include + +#import + +#include "pugl_internal.h" + +@interface PuglWindow : NSWindow +{ +@public + PuglView* puglview; +} + +- (id) initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag; +- (void) setPuglview:(PuglView*)view; +- (BOOL) windowShouldClose:(id)sender; +@end + +@implementation PuglWindow + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(unsigned int)aStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)flag +{ + NSWindow* result = [super initWithContentRect:contentRect + styleMask:(NSClosableWindowMask | + NSTitledWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered defer:NO]; + + [result setAcceptsMouseMovedEvents:YES]; + [result setLevel: CGShieldingWindowLevel() + 1]; + + return result; +} + +- (void)setPuglview:(PuglView*)view +{ + puglview = view; + [self setContentSize:NSMakeSize(view->width, view->height) ]; +} + +- (BOOL)windowShouldClose:(id)sender +{ + if (puglview->closeFunc) + puglview->closeFunc(puglview); + return YES; +} + +@end + +void +puglDisplay(PuglView* view) +{ + if (view->displayFunc) { + view->displayFunc(view); + } +} + +@interface PuglOpenGLView : NSOpenGLView +{ + int colorBits; + int depthBits; +@public + PuglView* puglview; + + NSTrackingArea* trackingArea; +} + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits; +- (void) reshape; +- (void) drawRect:(NSRect)rect; +- (void) mouseMoved:(NSEvent*)event; +- (void) mouseDragged:(NSEvent*)event; +- (void) mouseDown:(NSEvent*)event; +- (void) mouseUp:(NSEvent*)event; +- (void) rightMouseDown:(NSEvent*)event; +- (void) rightMouseUp:(NSEvent*)event; +- (void) keyDown:(NSEvent*)event; +- (void) keyUp:(NSEvent*)event; +- (void) flagsChanged:(NSEvent*)event; + +@end + +@implementation PuglOpenGLView + +- (id) initWithFrame:(NSRect)frame + colorBits:(int)numColorBits + depthBits:(int)numDepthBits +{ + colorBits = numColorBits; + depthBits = numDepthBits; + + NSOpenGLPixelFormatAttribute pixelAttribs[16] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, + colorBits, + NSOpenGLPFADepthSize, + depthBits, + 0 + }; + + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] + initWithAttributes:pixelAttribs]; + + if (pixelFormat) { + self = [super initWithFrame:frame pixelFormat:pixelFormat]; + [pixelFormat release]; + if (self) { + [[self openGLContext] makeCurrentContext]; + [self reshape]; + } + } else { + self = nil; + } + + return self; +} + +- (void) reshape +{ + [[self openGLContext] update]; + + NSRect bounds = [self bounds]; + int width = bounds.size.width; + int height = bounds.size.height; + + if (puglview) { + /* NOTE: Apparently reshape gets called when the GC gets around to + deleting the view (?), so we must have reset puglview to NULL when + this comes around. + */ + if (puglview->reshapeFunc) { + puglview->reshapeFunc(puglview, width, height); + } else { + puglDefaultReshape(puglview, width, height); + } + + puglview->width = width; + puglview->height = height; + } +} + +- (void) drawRect:(NSRect)rect +{ + puglDisplay(puglview); + glFlush(); + glSwapAPPLE(); +} + +static unsigned +getModifiers(PuglView* view, NSEvent* ev) +{ + const unsigned modifierFlags = [ev modifierFlags]; + + view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); + + unsigned mods = 0; + mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; + mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; + mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; + mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; + return mods; +} + +-(void)updateTrackingAreas +{ + if (trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const int opts = (NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways); + trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds] + options:opts + owner:self + userInfo:nil]; + [self addTrackingArea:trackingArea]; +} + +- (void)mouseEntered:(NSEvent*)theEvent +{ + [self updateTrackingAreas]; +} + +- (void)mouseExited:(NSEvent*)theEvent +{ +} + +- (void) mouseMoved:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDragged:(NSEvent*)event +{ + if (puglview->motionFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->motionFunc(puglview, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, true, loc.x, puglview->height - loc.y); + } +} + +- (void) mouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 1, false, loc.x, puglview->height - loc.y); + } + [self updateTrackingAreas]; +} + +- (void) rightMouseDown:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, true, loc.x, puglview->height - loc.y); + } +} + +- (void) rightMouseUp:(NSEvent*)event +{ + if (puglview->mouseFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->mouseFunc(puglview, 3, false, loc.x, puglview->height - loc.y); + } +} + +- (void) scrollWheel:(NSEvent*)event +{ + if (puglview->scrollFunc) { + NSPoint loc = [event locationInWindow]; + puglview->mods = getModifiers(puglview, event); + puglview->scrollFunc(puglview, + loc.x, puglview->height - loc.y, + [event deltaX], [event deltaY]); + } + [self updateTrackingAreas]; +} + +- (void) keyDown:(NSEvent*)event +{ + if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); + } +} + +- (void) keyUp:(NSEvent*)event +{ + if (puglview->keyboardFunc) { + NSString* chars = [event characters]; + puglview->mods = getModifiers(puglview, event); + puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); + } +} + +- (void) flagsChanged:(NSEvent*)event +{ + if (puglview->specialFunc) { + const unsigned mods = getModifiers(puglview, [event modifierFlags]); + if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); + } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); + } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); + } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { + puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); + } + puglview->mods = mods; + } +} + +@end + +struct PuglInternalsImpl { + PuglOpenGLView* glview; + id window; +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + [NSAutoreleasePool new]; + [NSApplication sharedApplication]; + + NSString* titleString = [[NSString alloc] + initWithBytes:title + length:strlen(title) + encoding:NSUTF8StringEncoding]; + + id window = [[PuglWindow new]retain]; + + [window setPuglview:view]; + [window setTitle:titleString]; + + impl->glview = [PuglOpenGLView new]; + impl->window = window; + impl->glview->puglview = view; + + [window setContentView:impl->glview]; + [NSApp activateIgnoringOtherApps:YES]; + [window makeFirstResponder:impl->glview]; + + [window makeKeyAndOrderFront:window]; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + view->impl->glview->puglview = NULL; + [view->impl->window close]; + [view->impl->glview release]; + [view->impl->window release]; + free(view->impl); + free(view); +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + [view->impl->glview setNeedsDisplay: YES]; + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->glview; +} diff --git a/pugl/pugl/pugl_win.cpp b/pugl/pugl/pugl_win.cpp new file mode 100644 index 0000000..5f85d12 --- /dev/null +++ b/pugl/pugl/pugl_win.cpp @@ -0,0 +1,376 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_win.cpp Windows/WGL Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include + +#include "pugl_internal.h" + +#ifndef WM_MOUSEWHEEL +# define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WHEEL_DELTA +# define WHEEL_DELTA 120 +#endif + +const int LOCAL_CLOSE_MSG = WM_USER + 50; + +struct PuglInternalsImpl { + HWND hwnd; + HDC hdc; + HGLRC hglrc; + WNDCLASS wc; +}; + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + // FIXME: This is nasty, and pugl should not have static anything. + // Should class be a parameter? Does this make sense on other platforms? + static int wc_count = 0; + char classNameBuf[256]; + _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d\n", title, wc_count++); + + impl->wc.style = CS_OWNDC; + impl->wc.lpfnWndProc = wndProc; + impl->wc.cbClsExtra = 0; + impl->wc.cbWndExtra = 0; + impl->wc.hInstance = 0; + impl->wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + impl->wc.hCursor = LoadCursor(NULL, IDC_ARROW); + impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + impl->wc.lpszMenuName = NULL; + impl->wc.lpszClassName = classNameBuf; + RegisterClass(&impl->wc); + + int winFlags = WS_POPUPWINDOW | WS_CAPTION; + if (resizable) { + winFlags |= WS_SIZEBOX; + } + + // Adjust the overall window size to accomodate our requested client size + RECT wr = { 0, 0, width, height }; + AdjustWindowRectEx(&wr, winFlags, FALSE, WS_EX_TOPMOST); + + impl->hwnd = CreateWindowEx( + WS_EX_TOPMOST, + classNameBuf, title, + (visible ? WS_VISIBLE : 0) | (parent ? WS_CHILD : winFlags), + 0, 0, wr.right-wr.left, wr.bottom-wr.top, + (HWND)parent, NULL, NULL, NULL); + + if (!impl->hwnd) { + free(impl); + free(view); + return NULL; + } + + SetWindowLongPtr(impl->hwnd, GWL_USERDATA, (LONG)view); + + impl->hdc = GetDC(impl->hwnd); + + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cDepthBits = 16; + pfd.iLayerType = PFD_MAIN_PLANE; + + int format = ChoosePixelFormat(impl->hdc, &pfd); + SetPixelFormat(impl->hdc, format, &pfd); + + impl->hglrc = wglCreateContext(impl->hdc); + wglMakeCurrent(impl->hdc, impl->hglrc); + + view->width = width; + view->height = height; + + return view; +} + +void +puglDestroy(PuglView* view) +{ + wglMakeCurrent(NULL, NULL); + wglDeleteContext(view->impl->hglrc); + ReleaseDC(view->impl->hwnd, view->impl->hdc); + DestroyWindow(view->impl->hwnd); + UnregisterClass(view->impl->wc.lpszClassName, NULL); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +void +puglDisplay(PuglView* view) +{ + wglMakeCurrent(view->impl->hdc, view->impl->hglrc); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + SwapBuffers(view->impl->hdc); + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(int sym) +{ + switch (sym) { + case VK_F1: return PUGL_KEY_F1; + case VK_F2: return PUGL_KEY_F2; + case VK_F3: return PUGL_KEY_F3; + case VK_F4: return PUGL_KEY_F4; + case VK_F5: return PUGL_KEY_F5; + case VK_F6: return PUGL_KEY_F6; + case VK_F7: return PUGL_KEY_F7; + case VK_F8: return PUGL_KEY_F8; + case VK_F9: return PUGL_KEY_F9; + case VK_F10: return PUGL_KEY_F10; + case VK_F11: return PUGL_KEY_F11; + case VK_F12: return PUGL_KEY_F12; + case VK_LEFT: return PUGL_KEY_LEFT; + case VK_UP: return PUGL_KEY_UP; + case VK_RIGHT: return PUGL_KEY_RIGHT; + case VK_DOWN: return PUGL_KEY_DOWN; + case VK_PRIOR: return PUGL_KEY_PAGE_UP; + case VK_NEXT: return PUGL_KEY_PAGE_DOWN; + case VK_HOME: return PUGL_KEY_HOME; + case VK_END: return PUGL_KEY_END; + case VK_INSERT: return PUGL_KEY_INSERT; + case VK_SHIFT: return PUGL_KEY_SHIFT; + case VK_CONTROL: return PUGL_KEY_CTRL; + case VK_MENU: return PUGL_KEY_ALT; + case VK_LWIN: return PUGL_KEY_SUPER; + case VK_RWIN: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) +{ + view->event_timestamp_ms = GetMessageTime(); + if (press) { + SetCapture(view->impl->hwnd); + } else { + ReleaseCapture(); + } + + if (view->mouseFunc) { + view->mouseFunc(view, button, press, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + } +} + +static void +setModifiers(PuglView* view) +{ + view->mods = 0; + view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; + view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; + view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; + view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; + view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; +} + +static LRESULT +handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + PuglKey key; + + setModifiers(view); + switch (message) { + case WM_CREATE: + case WM_SHOWWINDOW: + case WM_SIZE: + RECT rect; + GetClientRect(view->impl->hwnd, &rect); + puglReshape(view, rect.right, rect.bottom); + view->width = rect.right; + view->height = rect.bottom; + break; + case WM_PAINT: + BeginPaint(view->impl->hwnd, &ps); + puglDisplay(view); + EndPaint(view->impl->hwnd, &ps); + break; + case WM_MOUSEMOVE: + if (view->motionFunc) { + view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + break; + case WM_LBUTTONDOWN: + processMouseEvent(view, 1, true, lParam); + break; + case WM_MBUTTONDOWN: + processMouseEvent(view, 2, true, lParam); + break; + case WM_RBUTTONDOWN: + processMouseEvent(view, 3, true, lParam); + break; + case WM_LBUTTONUP: + processMouseEvent(view, 1, false, lParam); + break; + case WM_MBUTTONUP: + processMouseEvent(view, 2, false, lParam); + break; + case WM_RBUTTONUP: + processMouseEvent(view, 3, false, lParam); + break; + case WM_MOUSEWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / (float)WHEEL_DELTA); + } + break; + case WM_MOUSEHWHEEL: + if (view->scrollFunc) { + view->scrollFunc( + view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), + (int16_t)HIWORD(wParam) / float(WHEEL_DELTA), 0); + } + break; + case WM_KEYDOWN: + view->event_timestamp_ms = (GetMessageTime()); + if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { + break; + } // else nobreak + case WM_KEYUP: + if ((key = keySymToSpecial(wParam))) { + if (view->specialFunc) { + view->specialFunc(view, message == WM_KEYDOWN, key); + } + } else if (view->keyboardFunc) { + view->keyboardFunc(view, message == WM_KEYDOWN, wParam); + } + break; + case WM_QUIT: + case LOCAL_CLOSE_MSG: + if (view->closeFunc) { + view->closeFunc(view); + } + break; + default: + return DefWindowProc( + view->impl->hwnd, message, wParam, lParam); + } + + return 0; +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + MSG msg; + while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { + handleMessage(view, msg.message, msg.wParam, msg.lParam); + } + + + if (view->redisplay) { + InvalidateRect(view->impl->hwnd, NULL, FALSE); + } + + return PUGL_SUCCESS; +} + +LRESULT CALLBACK +wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWL_USERDATA); + switch (message) { + case WM_CREATE: + PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); + return 0; + case WM_CLOSE: + PostMessage(hwnd, LOCAL_CLOSE_MSG, wParam, lParam); + return 0; + case WM_DESTROY: + return 0; + default: + if (view) { + return handleMessage(view, message, wParam, lParam); + } else { + return DefWindowProc(hwnd, message, wParam, lParam); + } + } +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return (PuglNativeWindow)view->impl->hwnd; +} diff --git a/pugl/pugl/pugl_x11.c b/pugl/pugl/pugl_x11.c new file mode 100644 index 0000000..c25b273 --- /dev/null +++ b/pugl/pugl/pugl_x11.c @@ -0,0 +1,386 @@ +/* + Copyright 2012-2014 David Robillard + Copyright 2011-2012 Ben Loftis, Harrison Consoles + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_x11.c X11 Pugl Implementation. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pugl_internal.h" + +struct PuglInternalsImpl { + Display* display; + int screen; + Window win; + GLXContext ctx; + Bool doubleBuffered; +}; + +/** + Attributes for single-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListSgl[] = { + GLX_RGBA, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +/** + Attributes for double-buffered RGBA with at least + 4 bits per color and a 16 bit depth buffer. +*/ +static int attrListDbl[] = { + GLX_RGBA, GLX_DOUBLEBUFFER, + GLX_RED_SIZE, 4, + GLX_GREEN_SIZE, 4, + GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, + None +}; + +PuglView* +puglCreate(PuglNativeWindow parent, + const char* title, + int width, + int height, + bool resizable, + bool visible) +{ + PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + if (!view || !impl) { + return NULL; + } + + view->impl = impl; + view->width = width; + view->height = height; + + impl->display = XOpenDisplay(0); + impl->screen = DefaultScreen(impl->display); + + XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); + if (!vi) { + vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); + impl->doubleBuffered = False; + printf("singlebuffered rendering will be used, no doublebuffering available\n"); + } else { + impl->doubleBuffered = True; + printf("doublebuffered rendering available\n"); + } + + int glxMajor, glxMinor; + glXQueryVersion(impl->display, &glxMajor, &glxMinor); + printf("GLX-Version %d.%d\n", glxMajor, glxMinor); + + impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); + + Window xParent = parent + ? (Window)parent + : RootWindow(impl->display, impl->screen); + + Colormap cmap = XCreateColormap( + impl->display, xParent, vi->visual, AllocNone); + + XSetWindowAttributes attr; + memset(&attr, 0, sizeof(XSetWindowAttributes)); + attr.colormap = cmap; + attr.border_pixel = 0; + + attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask + | ButtonPressMask | ButtonReleaseMask + | PointerMotionMask | StructureNotifyMask; + + impl->win = XCreateWindow( + impl->display, xParent, + 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, + CWBorderPixel | CWColormap | CWEventMask, &attr); + + XSizeHints sizeHints; + memset(&sizeHints, 0, sizeof(sizeHints)); + if (!resizable) { + sizeHints.flags = PMinSize|PMaxSize; + sizeHints.min_width = width; + sizeHints.min_height = height; + sizeHints.max_width = width; + sizeHints.max_height = height; + XSetNormalHints(impl->display, impl->win, &sizeHints); + } + + if (title) { + XStoreName(impl->display, impl->win, title); + } + + if (!parent) { + Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); + } + + if (visible) { + XMapRaised(impl->display, impl->win); + } + + if (glXIsDirect(impl->display, impl->ctx)) { + printf("DRI enabled\n"); + } else { + printf("No DRI available\n"); + } + + XFree(vi); + + return view; +} + +void +puglDestroy(PuglView* view) +{ + if (!view) { + return; + } + + glXDestroyContext(view->impl->display, view->impl->ctx); + XDestroyWindow(view->impl->display, view->impl->win); + XCloseDisplay(view->impl->display); + free(view->impl); + free(view); +} + +static void +puglReshape(PuglView* view, int width, int height) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + + if (view->reshapeFunc) { + view->reshapeFunc(view, width, height); + } else { + puglDefaultReshape(view, width, height); + } + + view->width = width; + view->height = height; +} + +static void +puglDisplay(PuglView* view) +{ + glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if (view->displayFunc) { + view->displayFunc(view); + } + + glFlush(); + if (view->impl->doubleBuffered) { + glXSwapBuffers(view->impl->display, view->impl->win); + } + + view->redisplay = false; +} + +static PuglKey +keySymToSpecial(KeySym sym) +{ + switch (sym) { + case XK_F1: return PUGL_KEY_F1; + case XK_F2: return PUGL_KEY_F2; + case XK_F3: return PUGL_KEY_F3; + case XK_F4: return PUGL_KEY_F4; + case XK_F5: return PUGL_KEY_F5; + case XK_F6: return PUGL_KEY_F6; + case XK_F7: return PUGL_KEY_F7; + case XK_F8: return PUGL_KEY_F8; + case XK_F9: return PUGL_KEY_F9; + case XK_F10: return PUGL_KEY_F10; + case XK_F11: return PUGL_KEY_F11; + case XK_F12: return PUGL_KEY_F12; + case XK_Left: return PUGL_KEY_LEFT; + case XK_Up: return PUGL_KEY_UP; + case XK_Right: return PUGL_KEY_RIGHT; + case XK_Down: return PUGL_KEY_DOWN; + case XK_Page_Up: return PUGL_KEY_PAGE_UP; + case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; + case XK_Home: return PUGL_KEY_HOME; + case XK_End: return PUGL_KEY_END; + case XK_Insert: return PUGL_KEY_INSERT; + case XK_Shift_L: return PUGL_KEY_SHIFT; + case XK_Shift_R: return PUGL_KEY_SHIFT; + case XK_Control_L: return PUGL_KEY_CTRL; + case XK_Control_R: return PUGL_KEY_CTRL; + case XK_Alt_L: return PUGL_KEY_ALT; + case XK_Alt_R: return PUGL_KEY_ALT; + case XK_Super_L: return PUGL_KEY_SUPER; + case XK_Super_R: return PUGL_KEY_SUPER; + } + return (PuglKey)0; +} + +static void +setModifiers(PuglView* view, unsigned xstate, unsigned xtime) +{ + view->event_timestamp_ms = xtime; + + view->mods = 0; + view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; + view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; + view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; + view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; +} + +static void +dispatchKey(PuglView* view, XEvent* event, bool press) +{ + KeySym sym; + char str[5]; + const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); + if (n == 0) { + return; + } else if (n > 1) { + fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); + return; + } + + const PuglKey special = keySymToSpecial(sym); + if (special && view->specialFunc) { + view->specialFunc(view, press, special); + } else if (!special && view->keyboardFunc) { + view->keyboardFunc(view, press, str[0]); + } +} + +PuglStatus +puglProcessEvents(PuglView* view) +{ + XEvent event; + while (XPending(view->impl->display) > 0) { + XNextEvent(view->impl->display, &event); + switch (event.type) { + case MapNotify: + puglReshape(view, view->width, view->height); + break; + case ConfigureNotify: + if ((event.xconfigure.width != view->width) || + (event.xconfigure.height != view->height)) { + puglReshape(view, + event.xconfigure.width, + event.xconfigure.height); + } + break; + case Expose: + if (event.xexpose.count != 0) { + break; + } + puglDisplay(view); + break; + case MotionNotify: + setModifiers(view, event.xmotion.state, event.xmotion.time); + if (view->motionFunc) { + view->motionFunc(view, event.xmotion.x, event.xmotion.y); + } + break; + case ButtonPress: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { + if (view->scrollFunc) { + float dx = 0, dy = 0; + switch (event.xbutton.button) { + case 4: dy = 1.0f; break; + case 5: dy = -1.0f; break; + case 6: dx = -1.0f; break; + case 7: dx = 1.0f; break; + } + view->scrollFunc(view, + event.xbutton.x, event.xbutton.y, + dx, dy); + } + break; + } + // nobreak + case ButtonRelease: + setModifiers(view, event.xbutton.state, event.xbutton.time); + if (view->mouseFunc && + (event.xbutton.button < 4 || event.xbutton.button > 7)) { + view->mouseFunc(view, + event.xbutton.button, event.type == ButtonPress, + event.xbutton.x, event.xbutton.y); + } + break; + case KeyPress: + setModifiers(view, event.xkey.state, event.xkey.time); + dispatchKey(view, &event, true); + break; + case KeyRelease: + setModifiers(view, event.xkey.state, event.xkey.time); + if (view->ignoreKeyRepeat && + XEventsQueued(view->impl->display, QueuedAfterReading)) { + XEvent next; + XPeekEvent(view->impl->display, &next); + if (next.type == KeyPress && + next.xkey.time == event.xkey.time && + next.xkey.keycode == event.xkey.keycode) { + XNextEvent(view->impl->display, &event); + break; + } + } + dispatchKey(view, &event, false); + break; + case ClientMessage: + if (!strcmp(XGetAtomName(view->impl->display, + event.xclient.message_type), + "WM_PROTOCOLS")) { + if (view->closeFunc) { + view->closeFunc(view); + } + } + break; + default: + break; + } + } + + if (view->redisplay) { + puglDisplay(view); + } + + return PUGL_SUCCESS; +} + +void +puglPostRedisplay(PuglView* view) +{ + view->redisplay = true; +} + +PuglNativeWindow +puglGetNativeWindow(PuglView* view) +{ + return view->impl->win; +} diff --git a/pugl/pugl_test.c b/pugl/pugl_test.c new file mode 100644 index 0000000..b9a54f5 --- /dev/null +++ b/pugl/pugl_test.c @@ -0,0 +1,193 @@ +/* + Copyright 2012 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_test.c A simple Pugl test that creates a top-level window. +*/ + +#include +#include + +#include "pugl/pugl.h" + +// Argh! +#ifdef __APPLE__ +# include +#else +# include +#endif + +static int quit = 0; +static float xAngle = 0.0f; +static float yAngle = 0.0f; +static float dist = 10.0f; + +static void +onReshape(PuglView* view, int width, int height) +{ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, width, height); + gluPerspective(45.0f, width/(float)height, 1.0f, 10.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +static void +onDisplay(PuglView* view) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + glTranslatef(0.0f, 0.0f, dist * -1); + glRotatef(xAngle, 0.0f, 1.0f, 0.0f); + glRotatef(yAngle, 1.0f, 0.0f, 0.0f); + + glBegin(GL_QUADS); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + + glColor3f(0, 0, 0); glVertex3f(-1, -1, -1); + glColor3f(0, 1, 0); glVertex3f(-1, 1, -1); + glColor3f(1, 1, 0); glVertex3f( 1, 1, -1); + glColor3f(1, 0, 0); glVertex3f( 1, -1, -1); + + glColor3f(0, 0, 1); glVertex3f(-1, -1, 1); + glColor3f(0, 1, 1); glVertex3f(-1, 1, 1); + glColor3f(1, 1, 1); glVertex3f( 1, 1, 1); + glColor3f(1, 0, 1); glVertex3f( 1, -1, 1); + + glEnd(); +} + +static void +printModifiers(PuglView* view) +{ + int mods = puglGetModifiers(view); + fprintf(stderr, "Modifiers:%s%s%s%s\n", + (mods & PUGL_MOD_SHIFT) ? " Shift" : "", + (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", + (mods & PUGL_MOD_ALT) ? " Alt" : "", + (mods & PUGL_MOD_SUPER) ? " Super" : ""); +} + +static void +onKeyboard(PuglView* view, bool press, uint32_t key) +{ + fprintf(stderr, "Key %c %s ", (char)key, press ? "down" : "up"); + printModifiers(view); + if (key == 'q' || key == 'Q' || key == PUGL_CHAR_ESCAPE || + key == PUGL_CHAR_DELETE || key == PUGL_CHAR_BACKSPACE) { + quit = 1; + } +} + +static void +onSpecial(PuglView* view, bool press, PuglKey key) +{ + fprintf(stderr, "Special key %d %s ", key, press ? "down" : "up"); + printModifiers(view); +} + +static void +onMotion(PuglView* view, int x, int y) +{ + xAngle = x % 360; + yAngle = y % 360; + puglPostRedisplay(view); +} + +static void +onMouse(PuglView* view, int button, bool press, int x, int y) +{ + fprintf(stderr, "Mouse %d %s at %d,%d ", + button, press ? "down" : "up", x, y); + printModifiers(view); +} + +static void +onScroll(PuglView* view, int x, int y, float dx, float dy) +{ + fprintf(stderr, "Scroll %d %d %f %f ", x, y, dx, dy); + printModifiers(view); + dist += dy / 4.0f; + puglPostRedisplay(view); +} + +static void +onClose(PuglView* view) +{ + quit = 1; +} + +int +main(int argc, char** argv) +{ + bool ignoreKeyRepeat = false; + bool resizable = false; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-h")) { + printf("USAGE: %s [OPTIONS]...\n\n" + " -h Display this help\n" + " -i Ignore key repeat\n" + " -r Resizable window\n", argv[0]); + return 0; + } else if (!strcmp(argv[i], "-i")) { + ignoreKeyRepeat = true; + } else if (!strcmp(argv[i], "-r")) { + resizable = true; + } else { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + } + } + + PuglView* view = puglCreate(0, "Pugl Test", 512, 512, resizable, true); + puglIgnoreKeyRepeat(view, ignoreKeyRepeat); + puglSetKeyboardFunc(view, onKeyboard); + puglSetMotionFunc(view, onMotion); + puglSetMouseFunc(view, onMouse); + puglSetScrollFunc(view, onScroll); + puglSetSpecialFunc(view, onSpecial); + puglSetDisplayFunc(view, onDisplay); + puglSetReshapeFunc(view, onReshape); + puglSetCloseFunc(view, onClose); + + while (!quit) { + puglProcessEvents(view); + } + + puglDestroy(view); + return 0; +} diff --git a/pugl/waf b/pugl/waf new file mode 100755 index 0000000..32ae229 Binary files /dev/null and b/pugl/waf differ diff --git a/pugl/wscript b/pugl/wscript new file mode 100644 index 0000000..3682d14 --- /dev/null +++ b/pugl/wscript @@ -0,0 +1,155 @@ +#!/usr/bin/env python +import glob +import os +import sys + +from waflib.extras import autowaf as autowaf +import waflib.Logs as Logs, waflib.Options as Options + +# Version of this package (even if built as a child) +PUGL_VERSION = '0.2.0' +PUGL_MAJOR_VERSION = '0' + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +# Pugl uses the same version number for both library and package +PUGL_LIB_VERSION = PUGL_VERSION + +# Variables for 'waf dist' +APPNAME = 'pugl' +VERSION = PUGL_VERSION + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + if Options.platform == 'win32': + opt.load('compiler_cxx') + autowaf.set_options(opt) + opt.add_option('--test', action='store_true', default=False, dest='build_tests', + help="Build unit tests") + opt.add_option('--static', action='store_true', default=False, dest='static', + help="Build static library") + opt.add_option('--shared', action='store_true', default=False, dest='shared', + help="Build shared library") + +def configure(conf): + conf.load('compiler_c') + if Options.platform == 'win32': + conf.load('compiler_cxx') + autowaf.configure(conf) + autowaf.display_header('Pugl Configuration') + + if conf.env['MSVC_COMPILER']: + conf.env.append_unique('CFLAGS', ['-TP', '-MD']) + else: + conf.env.append_unique('CFLAGS', '-std=c99') + + # Shared library building is broken on win32 for some reason + conf.env['BUILD_TESTS'] = Options.options.build_tests + conf.env['BUILD_SHARED'] = (Options.platform != 'win32' or + Options.options.shared) + conf.env['BUILD_STATIC'] = (Options.options.build_tests or + Options.options.static) + + autowaf.define(conf, 'PUGL_VERSION', PUGL_VERSION) + conf.write_config_header('pugl_config.h', remove=False) + + conf.env['INCLUDES_PUGL'] = ['%s/pugl-%s' % (conf.env['INCLUDEDIR'], + PUGL_MAJOR_VERSION)] + conf.env['LIBPATH_PUGL'] = [conf.env['LIBDIR']] + conf.env['LIB_PUGL'] = ['pugl-%s' % PUGL_MAJOR_VERSION]; + + autowaf.display_msg(conf, "Static library", str(conf.env['BUILD_STATIC'])) + autowaf.display_msg(conf, "Unit tests", str(conf.env['BUILD_TESTS'])) + print('') + +def build(bld): + # C Headers + includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION + bld.install_files(includedir, bld.path.ant_glob('pugl/*.h')) + + # Pkgconfig file + autowaf.build_pc(bld, 'PUGL', PUGL_VERSION, PUGL_MAJOR_VERSION, [], + {'PUGL_MAJOR_VERSION' : PUGL_MAJOR_VERSION}) + + libflags = [ '-fvisibility=hidden' ] + framework = [] + libs = [] + if Options.platform == 'win32': + lang = 'cxx' + lib_source = ['pugl/pugl_win.cpp'] + libs = ['opengl32', 'glu32', 'gdi32', 'user32'] + defines = [] + elif Options.platform == 'darwin': + lang = 'c' # Objective C, actually + lib_source = ['pugl/pugl_osx.m'] + framework = ['Cocoa', 'OpenGL'] + defines = [] + else: + lang = 'c' + lib_source = ['pugl/pugl_x11.c'] + libs = ['X11', 'GL', 'GLU'] + defines = [] + if bld.env['MSVC_COMPILER']: + libflags = [] + + # Shared Library + if bld.env['BUILD_SHARED']: + obj = bld(features = '%s %sshlib' % (lang, lang), + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + framework = framework, + name = 'libpugl', + target = 'pugl-%s' % PUGL_MAJOR_VERSION, + vnum = PUGL_LIB_VERSION, + install_path = '${LIBDIR}', + defines = defines, + cflags = libflags + [ '-DPUGL_SHARED', + '-DPUGL_INTERNAL' ]) + + # Static library + if bld.env['BUILD_STATIC']: + obj = bld(features = '%s %sstlib' % (lang, lang), + export_includes = ['.'], + source = lib_source, + includes = ['.', './src'], + lib = libs, + framework = framework, + name = 'libpugl_static', + target = 'pugl-%s' % PUGL_MAJOR_VERSION, + vnum = PUGL_LIB_VERSION, + install_path = '${LIBDIR}', + defines = defines, + cflags = ['-DPUGL_INTERNAL']) + + if bld.env['BUILD_TESTS']: + test_libs = libs + test_cflags = [''] + + # Unit test program + obj = bld(features = 'c cprogram', + source = 'pugl_test.c', + includes = ['.', './src'], + use = 'libpugl_static', + lib = test_libs, + framework = framework, + target = 'pugl_test', + install_path = '', + defines = defines, + cflags = test_cflags) + +def lint(ctx): + subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* pugl/*', shell=True) + +from waflib import TaskGen +@TaskGen.extension('.m') +def m_hook(self, node): + "Alias .m files to be compiled the same as .c files, gcc will do the right thing." + return self.create_compiled_task('c', node) -- cgit v1.2.3