Apply patch [#20145] Ghost Win32 roundup patch: Minimum Window Size, Continuous Grab...
authorNathan Letwory <nathan@letworyinteractive.com>
Wed, 2 Dec 2009 00:57:12 +0000 (00:57 +0000)
committerNathan Letwory <nathan@letworyinteractive.com>
Wed, 2 Dec 2009 00:57:12 +0000 (00:57 +0000)
This nice patch by Matt D. (matd in #blendercoders) adds three nice features that can be seen already in the other supported OSes:

* minimum window size: to prevent some bugs with the window manager of Blender, system windows cannot be resized smaller than the minimum size.

* Continuous Grab is finally in Windows! Default settings since alpha 0 already have the feature enabled by default, so grab a new build and enjoy :)

* GHOST support for drag and drop added. This prepares Blender for drag and drop from OS -> Blender. Currently not very useful, since wm needs to be readied for that. But it does work (do BF_GHOST_DEBUG=1 build and drag a file onto a Blender window).

Thanks Matt D.!

12 files changed:
config/win32-mingw-config.py
intern/ghost/SConscript
intern/ghost/intern/GHOST_Debug.h
intern/ghost/intern/GHOST_DropTargetWin32.cpp [new file with mode: 0644]
intern/ghost/intern/GHOST_DropTargetWin32.h [new file with mode: 0644]
intern/ghost/intern/GHOST_EventPrinter.cpp
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_SystemWin32.h
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.h
source/blender/windowmanager/intern/wm_cursors.c
tools/btools.py

index 709261ec1cac7ae3b51c3ee79034d0779a7d0780..0f07ca4c2ee4ef8611e7a216622599b66a07aeb2 100644 (file)
@@ -145,7 +145,7 @@ C_WARN = [ '-Wno-char-subscripts', '-Wdeclaration-after-statement' ]
 
 CC_WARN = [ '-Wall' ]
 
-LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++']
+LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid']
 
 BF_DEBUG = False
 BF_DEBUG_CCFLAGS= ['-g']
index 09da6f94ddcb1272e9f9a9d2e6f00d829b843c10..84ca97fdb93419c70d349f560237c477428de239 100644 (file)
@@ -37,8 +37,12 @@ else:
        print "Unknown window system specified."
        Exit()
 
+defs=['_USE_MATH_DEFINES']
+if env['BF_GHOST_DEBUG']:
+       defs.append('BF_GHOST_DEBUG')
+       
 incs = '. ../string ' + env['BF_OPENGL_INC']
 if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
        incs = env['BF_WINTAB_INC'] + ' ' + incs
-env.BlenderLib ('bf_ghost', sources, Split(incs), defines=['_USE_MATH_DEFINES'], libtype=['intern','player'], priority = [40,15] ) 
+env.BlenderLib ('bf_ghost', sources, Split(incs), defines=defs, libtype=['intern','player'], priority = [40,15] ) 
 
index 7f836202720df180078b884347475f124367c9b6..1ca4ce2b6de69f2f5811eba6f85922739aa17be5 100644 (file)
 #ifdef WIN32
        #ifdef _DEBUG
                #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
-               #define GHOST_DEBUG
+               // #define GHOST_DEBUG
        #endif // _DEBUG
 #endif // WIN32
 
+#ifdef BF_GHOST_DEBUG 
+       #define GHOST_DEBUG // spit ghost events to stdout
+#endif // BF_GHOST_DEBUG 
+
 #ifdef GHOST_DEBUG
        #include <iostream>
+       #include <stdio.h> //for printf()
 #endif // GHOST_DEBUG
 
 
diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cpp b/intern/ghost/intern/GHOST_DropTargetWin32.cpp
new file mode 100644 (file)
index 0000000..d757703
--- /dev/null
@@ -0,0 +1,426 @@
+/**
+ * $Id: $
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#include "GHOST_Debug.h"
+#include "GHOST_DropTargetWin32.h"
+
+#ifdef GHOST_DEBUG
+// utility
+void printLastError(void);
+#endif // GHOST_DEBUG
+
+
+GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 * window, GHOST_SystemWin32 * system)
+:
+m_window(window),
+m_system(system)
+{
+       m_cRef = 1;
+       m_hWnd = window->getHWND();
+       m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+       
+       // register our window as drop target
+       ::RegisterDragDrop(m_hWnd, this);
+}
+
+GHOST_DropTargetWin32::~GHOST_DropTargetWin32()
+{
+       ::RevokeDragDrop(m_hWnd);
+}
+
+
+/* 
+ *     IUnknown::QueryInterface
+ */
+HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface (REFIID riid, void ** ppvObj)
+{
+
+       if (!ppvObj)
+               return E_INVALIDARG;
+       *ppvObj = NULL;
+
+       if(riid == IID_IUnknown || riid == IID_IDropTarget)
+       {
+               AddRef();
+               *ppvObj = (void*)this;
+               return S_OK;
+       }
+       else
+       {
+               *ppvObj = 0;
+               return E_NOINTERFACE;
+       }
+}
+
+
+/* 
+ *     IUnknown::AddRef 
+ */
+
+ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
+{
+       return ::InterlockedIncrement(&m_cRef);
+}
+
+/* 
+ * IUnknown::Release
+ */
+ULONG __stdcall GHOST_DropTargetWin32::Release(void)
+{
+       ULONG refs = ::InterlockedDecrement(&m_cRef);
+               
+       if(refs == 0)
+       {
+               delete this;
+               return 0;
+       }
+       else
+       {
+               return refs;
+       }
+}
+
+/* 
+ * Implementation of IDropTarget::DragEnter
+ */
+HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
+{
+       // we don't know yet if we accept the drop.
+       m_window->setAcceptDragOperation(false);
+       *pdwEffect = DROPEFFECT_NONE;
+       
+       m_draggedObjectType = getGhostType(pDataObject);
+       m_system->pushDragDropEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
+       return S_OK;
+}
+
+/* 
+ * Implementation of IDropTarget::DragOver
+ */
+HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
+{
+       if(m_window->canAcceptDragOperation())
+       {
+               *pdwEffect = allowedDropEffect(*pdwEffect);
+       }
+       else
+       {
+               *pdwEffect = DROPEFFECT_NONE;
+               //*pdwEffect = DROPEFFECT_COPY; // XXX Uncomment to test drop. Drop will not be called if pdwEffect == DROPEFFECT_NONE.
+       }
+       m_system->pushDragDropEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
+       return S_OK;
+}
+
+/* 
+ * Implementation of IDropTarget::DragLeave
+ */
+HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
+{
+       m_system->pushDragDropEvent(GHOST_kEventDraggingExited, m_draggedObjectType, m_window, 0, 0, NULL);
+       m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+       return S_OK;
+}
+
+/* Implementation of IDropTarget::Drop
+ * This function will not be called if pdwEffect is set to DROPEFFECT_NONE in 
+ * the implementation of IDropTarget::DragOver
+ */
+HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
+{
+       void * data = getGhostData(pDataObject);
+       if(m_window->canAcceptDragOperation())
+       {
+               *pdwEffect = allowedDropEffect(*pdwEffect);
+
+       }
+       else
+       {
+               *pdwEffect = DROPEFFECT_NONE;
+       }
+       if (data)
+               m_system->pushDragDropEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, pt.x, pt.y, data );
+               
+       m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
+       return S_OK;
+}
+
+/* 
+ * Helpers
+ */
+DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dwAllowed)
+{
+       DWORD dwEffect = DROPEFFECT_NONE;
+       if(dwAllowed & DROPEFFECT_COPY) 
+               dwEffect = DROPEFFECT_COPY;
+
+       return dwEffect;
+}
+
+GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject * pDataObject)
+{
+       /* Text
+        * Note: Unicode text is aviable as CF_TEXT too, the system can do the 
+        * conversion, but we do the conversion ourself with WC_NO_BEST_FIT_CHARS.
+        */
+       FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       if(pDataObject->QueryGetData(&fmtetc) == S_OK)
+       {
+               return GHOST_kDragnDropTypeString;
+       }
+
+       // Filesnames
+       fmtetc.cfFormat = CF_HDROP;
+       if(pDataObject->QueryGetData(&fmtetc) == S_OK)
+       {
+               return GHOST_kDragnDropTypeFilenames;
+       }
+
+       return GHOST_kDragnDropTypeUnknown;
+}
+
+void * GHOST_DropTargetWin32::getGhostData(IDataObject * pDataObject)
+{
+       GHOST_TDragnDropTypes type = getGhostType(pDataObject);
+       switch(type)
+       {
+               case GHOST_kDragnDropTypeFilenames:
+                       return getDropDataAsFilenames(pDataObject);
+                       break;
+               case GHOST_kDragnDropTypeString:
+                       return getDropDataAsString(pDataObject);
+                       break;
+               case GHOST_kDragnDropTypeBitmap:
+                       //return getDropDataAsBitmap(pDataObject);
+                       break;
+               default:
+#ifdef GHOST_DEBUG
+                       ::printf("\nGHOST_kDragnDropTypeUnknown");
+#endif // GHOST_DEBUG
+                       return NULL;
+                       break;
+       }
+       return NULL;
+}
+
+void * GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject * pDataObject)
+{
+       UINT  totfiles, nvalid=0;
+       WCHAR fpath [MAX_PATH]; 
+       char * temp_path;
+       GHOST_TStringArray *strArray = NULL;
+       FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       STGMEDIUM stgmed;
+       HDROP hdrop;
+
+       // Check if dataobject supplies the format we want.
+       // Double checking here, first in getGhostType.
+       if(pDataObject->QueryGetData(&fmtetc) == S_OK)
+       {
+               if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
+               {
+                       hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
+
+                       totfiles = ::DragQueryFileW ( hdrop, -1, NULL, 0 );
+                       if (!totfiles)
+                       {
+                               ::GlobalUnlock(stgmed.hGlobal);
+                               return NULL;
+                       }
+
+                       strArray = (GHOST_TStringArray*) ::malloc(sizeof(GHOST_TStringArray));
+                       strArray->count = 0;
+                       strArray->strings = (GHOST_TUns8**) ::malloc(totfiles*sizeof(GHOST_TUns8*));
+
+                       for ( UINT nfile = 0; nfile < totfiles; nfile++ )
+                       {
+                               if ( ::DragQueryFileW ( hdrop, nfile, fpath, MAX_PATH ) > 0 )
+                               {
+                                       if ( !WideCharToANSI(fpath, temp_path) )
+                                       {
+                                               continue;
+                                       } 
+                                       // Just ignore paths that could not be converted verbatim.
+                                       if (strpbrk(temp_path, "?"))
+                                       {
+#ifdef GHOST_DEBUG
+                                               ::printf("\ndiscarding path that contains illegal characters: %s", temp_path);
+#endif // GHOST_DEBUG
+                                               ::free(temp_path);
+                                               temp_path = NULL;
+                                               continue;
+                                       }
+                                       strArray->strings[nvalid] = (GHOST_TUns8*) temp_path;
+                                       strArray->count = nvalid+1;
+                                       nvalid++;
+                               }
+                       }
+                       // Free up memory.
+                       ::GlobalUnlock(stgmed.hGlobal);
+                       ::ReleaseStgMedium(&stgmed);
+                       
+                       return strArray;
+               }
+       }
+       return NULL;
+}
+
+void * GHOST_DropTargetWin32::getDropDataAsString(IDataObject * pDataObject)
+{
+       char* tmp_string;
+       FORMATETC fmtetc = { CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+       STGMEDIUM stgmed;
+
+       // Try unicode first.
+       // Check if dataobject supplies the format we want.
+       if(pDataObject->QueryGetData(&fmtetc) == S_OK)
+       {
+               if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
+               {
+                       LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
+                       if ( !WideCharToANSI(wstr, tmp_string) )
+                       {
+                               ::GlobalUnlock(stgmed.hGlobal);
+                               return NULL;
+                       }
+                       // Free memory
+                       ::GlobalUnlock(stgmed.hGlobal);
+                       ::ReleaseStgMedium(&stgmed);
+#ifdef GHOST_DEBUG
+                       ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",tmp_string);
+#endif // GHOST_DEBUG
+                       return tmp_string;
+               }
+       }
+
+       fmtetc.cfFormat = CF_TEXT;
+
+       if(pDataObject->QueryGetData(&fmtetc) == S_OK)
+       {
+               if(pDataObject->GetData(&fmtetc, &stgmed) == S_OK)
+               {
+                       char * str = (char*)::GlobalLock(stgmed.hGlobal);
+                       
+                       tmp_string = (char*)::malloc(::strlen(str)+1);
+                       if ( !tmp_string )
+                       {
+                               ::GlobalUnlock(stgmed.hGlobal);
+                               return NULL;
+                       }
+
+                       if ( !::strcpy(tmp_string, str) )
+                       {
+                               ::free(tmp_string);
+                               ::GlobalUnlock(stgmed.hGlobal);
+                               return NULL;
+                       }
+                       // Free memory
+                       ::GlobalUnlock(stgmed.hGlobal);
+                       ::ReleaseStgMedium(&stgmed);
+
+                       return tmp_string;
+               }
+       }
+       
+       return NULL;
+}
+
+int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char * &out)
+{
+       int size;
+       out = NULL; //caller should free if != NULL
+
+       // Get the required size.
+       size = ::WideCharToMultiByte(CP_ACP,            //System Default Codepage
+                                                               0x00000400,             // WC_NO_BEST_FIT_CHARS
+                                                               in,
+                                                               -1,                             //-1 null terminated, makes output null terminated too.
+                                                               NULL,
+                                                               0,
+                                                               NULL,NULL
+                       );
+       
+       if(!size) 
+       {
+#ifdef GHOST_DEBUG
+               ::printLastError();
+#endif // GHOST_DEBUG
+               return 0;
+       }
+
+       out = (char*)::malloc(size);
+       if (!out)
+       {
+               ::printf("\nmalloc failed!!!");
+               return 0;
+       }
+
+       size = ::WideCharToMultiByte(CP_ACP,
+                                                               0x00000400,
+                                                               in,
+                                                               -1,
+                                                               (LPSTR) out,
+                                                               size,
+                                                               NULL,NULL
+                       );
+
+       if(!size)
+       {
+#ifdef GHOST_DEBUG
+               ::printLastError();
+#endif //GHOST_DEBUG
+               ::free(out);
+               out = NULL;
+       }
+       return size;
+}
+
+#ifdef GHOST_DEBUG
+void printLastError(void)
+{
+       LPTSTR s;
+       DWORD err;
+
+       err = GetLastError();
+       if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+               FORMAT_MESSAGE_FROM_SYSTEM,
+               NULL,
+               err,
+               0,
+               (LPTSTR)&s,
+               0,
+               NULL)
+       )
+       {
+               printf("\nLastError: (%d) %s\n", (int)err, s);
+               LocalFree(s);
+       }
+}
+#endif // GHOST_DEBUG
+
diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.h b/intern/ghost/intern/GHOST_DropTargetWin32.h
new file mode 100644 (file)
index 0000000..6ea6ed8
--- /dev/null
@@ -0,0 +1,155 @@
+/**
+ * $Id: $
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef _GHOST_DROP_TARGET_WIN32_H_
+#define _GHOST_DROP_TARGET_WIN32_H_
+
+#include <windows.h>
+#include <string.h>
+#include <GHOST_Types.h>
+#include "GHOST_WindowWin32.h"
+#include "GHOST_SystemWin32.h"
+
+class GHOST_DropTargetWin32 : public IDropTarget
+{
+public:
+       /* IUnknownd implementation.
+        * Enables clients to get pointers to other interfaces on a given object 
+        * through the QueryInterface method, and manage the existence of the object
+        * through the AddRef and Release methods. All other COM interfaces are 
+        * inherited, directly or indirectly, from IUnknown. Therefore, the three 
+        * methods in IUnknown are the first entries in the VTable for every interface. 
+        */
+       HRESULT __stdcall QueryInterface (REFIID riid, void ** ppvObj);
+       ULONG   __stdcall AddRef (void);
+       ULONG   __stdcall Release (void);
+
+       /* IDropTarget implementation
+        + The IDropTarget interface is one of the interfaces you implement to 
+        provide drag-and-drop operations in your application. It contains methods 
+        used in any application that can be a target for data during a 
+        drag-and-drop operation. A drop-target application is responsible for:
+        * 
+        *      - Determining the effect of the drop on the target application.
+        *      - Incorporating any valid dropped data when the drop occurs.
+        *      - Communicating target feedback to the source so the source application
+        *        can provide appropriate visual feedback such as setting the cursor.
+        *      - Implementing drag scrolling.
+        *      - Registering and revoking its application windows as drop targets.
+        * 
+        * The IDropTarget interface contains methods that handle all these 
+        * responsibilities except registering and revoking the application window 
+        * as a drop target, for which you must call the RegisterDragDrop and the 
+        * RevokeDragDrop functions.
+        */
+       
+       HRESULT __stdcall DragEnter (IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
+       HRESULT __stdcall DragOver (DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
+       HRESULT __stdcall DragLeave (void);
+       HRESULT __stdcall Drop (IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect);
+
+       /**
+        * Constructor
+        * With the modifier keys, we want to distinguish left and right keys.
+        * Sometimes this is not possible (Windows ME for instance). Then, we want
+        * events generated for both keys.
+        * @param window        The window to register as drop target.
+        * @param system        The associated system.
+        */
+       GHOST_DropTargetWin32(GHOST_WindowWin32 * window, GHOST_SystemWin32 * system);
+
+       /**
+        * Destructor
+        * Do NOT destroy directly. Use Release() instead to make COM happy.
+        */
+       ~GHOST_DropTargetWin32();
+
+private:
+
+       /* Internal helper functions */
+
+       /**
+        * Base the effect on those allowed by the dropsource.
+        * @param dwAllowed Drop sources allowed drop effect.
+        * @return The allowed drop effect.
+        */
+       DWORD allowedDropEffect(DWORD dwAllowed);
+
+       /**
+        * Query DataObject for the data types it supports.
+        * @param pDataObject Pointer to the DataObject.
+        * @return GHOST data type.
+        */
+       GHOST_TDragnDropTypes getGhostType(IDataObject * pDataObject);
+
+       /**
+        * Get data to pass in event.
+        * It checks the type and calls specific functions for each type.
+        * @param pDataObject Pointer to the DataObject.
+        * @return Pointer to data.
+        */
+       void * getGhostData(IDataObject * pDataObject);
+
+       /**
+        * Allocate data as file array to pass in event.
+        * @param pDataObject Pointer to the DataObject.
+        * @return Pointer to data.
+        */
+       void * getDropDataAsFilenames(IDataObject * pDataObject);
+
+       /**
+        * Allocate data as string to pass in event.
+        * @param pDataObject Pointer to the DataObject.
+        * @return Pointer to data.
+        */
+       void * getDropDataAsString(IDataObject * pDataObject);
+
+       /**
+        * Convert Unicode to ANSI, replacing unconvertable chars with '?'.
+        * The ANSI codepage is the system default codepage, 
+        * and can change from system to system.
+        * @param in LPCWSTR.
+        * @param out char *. Is set to NULL on failure.
+        * @return 0 on failure. Else the size of the string including '\0'.
+        */
+       int WideCharToANSI(LPCWSTR in, char * &out);
+
+       /* Private member variables */
+       /* COM reference count. */
+       LONG    m_cRef; 
+       /* Handle of the associated window. */
+       HWND    m_hWnd;
+       /* The associated GHOST_WindowWin32. */
+       GHOST_WindowWin32 * m_window;
+       /* The System. */
+       GHOST_SystemWin32 * m_system;
+       /* Data type of the dragged object */
+       GHOST_TDragnDropTypes m_draggedObjectType;
+
+};
+
+#endif  // _GHOST_DROP_TARGET_WIN32_H_
index c6b3416669ea6844dbf44be4dc01e421d4d7e3d1..91b554744410d97d1e7f152a4ee6fce98681185f 100644 (file)
@@ -49,7 +49,7 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent* event)
 
        if (event->getType() == GHOST_kEventWindowUpdate) return false;
 
-       std::cout << "GHOST_EventPrinter::processEvent, time: " << (GHOST_TInt32)event->getTime() << ", type: ";
+       std::cout << "\nGHOST_EventPrinter::processEvent, time: " << (GHOST_TInt32)event->getTime() << ", type: ";
        switch (event->getType()) {
        case GHOST_kEventUnknown:
                std::cout << "GHOST_kEventUnknown"; handled = false;    
@@ -125,19 +125,21 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent* event)
        case GHOST_kEventDraggingDropDone:
                {
                        GHOST_TEventDragnDropData* dragnDropData = (GHOST_TEventDragnDropData*)((GHOST_IEvent*)event)->getData();
-                       std::cout << "GHOST_kEventDraggingDropDone, dragged object type : " << dragnDropData->dataType;
+                       std::cout << "GHOST_kEventDraggingDropDone,";
                        std::cout << " mouse at x=" << dragnDropData->x << " y=" << dragnDropData->y;
                        switch (dragnDropData->dataType) {
                                case GHOST_kDragnDropTypeString:
-                                       std::cout << " string received = " << (char*)dragnDropData->data;
+                                       std::cout << " type : GHOST_kDragnDropTypeString,";
+                                       std::cout << "\n  String received = " << (char*)dragnDropData->data;
                                        break;
                                case GHOST_kDragnDropTypeFilenames:
                                {
                                        GHOST_TStringArray *strArray = (GHOST_TStringArray*)dragnDropData->data;
                                        int i;
-                                       std::cout << "\nReceived " << strArray->count << " filenames";
+                                       std::cout << " type : GHOST_kDragnDropTypeFilenames,";
+                                       std::cout << "\n  Received " << strArray->count << " filename" << (strArray->count > 1 ? "s:" : ":");
                                        for (i=0;i<strArray->count;i++)
-                                               std::cout << " Filename #" << i << ": " << strArray->strings[i];
+                                               std::cout << "\n        File[" << i << "] : " << strArray->strings[i];
                                }
                                        break;
                                default:
@@ -192,7 +194,6 @@ bool GHOST_EventPrinter::processEvent(GHOST_IEvent* event)
                std::cout << "not found"; handled = false; 
                break;
        }
-       std::cout << "\n";
        return handled;
 }
 
index 8a17d4556958a576ee6226fddfaaa2f23677e840..15914a5bf4343501c82913a81300550b11b44553 100644 (file)
@@ -39,6 +39,7 @@
 #endif
 
 #include "GHOST_SystemWin32.h"
+#include "GHOST_EventDragnDrop.h"
 
 // win64 doesn't define GWL_USERDATA
 #ifdef WIN32
@@ -138,10 +139,15 @@ GHOST_SystemWin32::GHOST_SystemWin32()
        m_displayManager = new GHOST_DisplayManagerWin32 ();
        GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
        m_displayManager->initialize();
+       
+       // Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
+       OleInitialize(0);
 }
 
 GHOST_SystemWin32::~GHOST_SystemWin32()
 {
+       // Shutdown COM
+       OleUninitialize();
 }
 
 
@@ -187,7 +193,7 @@ GHOST_IWindow* GHOST_SystemWin32::createWindow(
        bool stereoVisual, const GHOST_TEmbedderWindowID parentWindow )
 {
        GHOST_Window* window = 0;
-       window = new GHOST_WindowWin32 (title, left, top, width, height, state, type, stereoVisual);
+       window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual);
        if (window) {
                if (window->getValid()) {
                        // Store the pointer to the window
@@ -248,10 +254,12 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
 GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
 {
        POINT point;
-       ::GetCursorPos(&point);
-       x = point.x;
-       y = point.y;
-       return GHOST_kSuccess;
+       if(::GetCursorPos(&point)){
+               x = point.x;
+               y = point.y;
+               return GHOST_kSuccess;
+       }
+       return GHOST_kFailure;
 }
 
 
@@ -499,11 +507,56 @@ GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
 }
 
 
-GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *window)
+GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
 {
-       GHOST_TInt32 x, y;
-       getSystem()->getCursorPosition(x, y);
-       return new GHOST_EventCursor (getSystem()->getMilliSeconds(), type, window, x, y);
+       GHOST_TInt32 x_screen, y_screen;
+       GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
+       GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
+       
+       system->getCursorPosition(x_screen, y_screen);
+
+       if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
+       {
+               GHOST_TInt32 x_new= x_screen;
+               GHOST_TInt32 y_new= y_screen;
+               GHOST_TInt32 x_accum, y_accum;
+               GHOST_Rect bounds;
+
+               /* fallback to window bounds */
+               if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
+                       window->getClientBounds(bounds);
+               }
+
+               /* could also clamp to screen bounds
+                * wrap with a window outside the view will fail atm  */
+
+               bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */
+
+               window->getCursorGrabAccum(x_accum, y_accum);
+               if(x_new != x_screen|| y_new != y_screen) {
+                       /* when wrapping we don't need to add an event because the
+                        * setCursorPosition call will cause a new event after */
+                       system->setCursorPosition(x_new, y_new); /* wrap */
+                       window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
+               }else{
+                       return new GHOST_EventCursor(system->getMilliSeconds(),
+                                                                                GHOST_kEventCursorMove,
+                                                                                window,
+                                                                                x_screen + x_accum,
+                                                                                y_screen + y_accum
+                       );
+               }
+
+       }
+       else {
+               return new GHOST_EventCursor(system->getMilliSeconds(),
+                                                                        GHOST_kEventCursorMove,
+                                                                        window,
+                                                                        x_screen,
+                                                                        y_screen
+               );
+       }
+       return NULL;
 }
 
 
@@ -549,6 +602,26 @@ GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_
        return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
 }
 
+GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, 
+                                                                                                       GHOST_TDragnDropTypes draggedObjectType,
+                                                                                                       GHOST_IWindow* window,
+                                                                                                       int mouseX, int mouseY,
+                                                                                                       void* data)
+{
+       GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
+       return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
+                                                                                                         eventType,
+                                                                                                         draggedObjectType,
+                                                                                                         window,mouseX,mouseY,data)
+                       );
+}
+
+void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
+{
+       minmax->ptMinTrackSize.x=320;
+       minmax->ptMinTrackSize.y=240;
+}
+
 
 LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
@@ -793,6 +866,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
                                        event = processWindowEvent(GHOST_kEventWindowUpdate, window);
                                        ::ValidateRect(hwnd, NULL);
                                        break;
+                               case WM_GETMINMAXINFO:
+                                       /* The WM_GETMINMAXINFO message is sent to a window when the size or 
+                                        * position of the window is about to change. An application can use 
+                                        * this message to override the window's default maximized size and 
+                                        * position, or its default minimum or maximum tracking size. 
+                                        */
+                                       processMinMaxInfo((MINMAXINFO *) lParam);
+                                       /* Let DefWindowProc handle it. */
+                                       break;
                                case WM_SIZE:
                                        /* The WM_SIZE message is sent to a window after its size has changed.
                                         * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
index 6a12975a3dd4ce8c8589e4b8046fbc29049b20bc..517f7ddbeea524657ed7147dd1917eb7fe7f37c2 100644 (file)
@@ -51,6 +51,7 @@ class GHOST_EventCursor;
 class GHOST_EventKey;
 class GHOST_EventWheel;
 class GHOST_EventWindow;
+class GHOST_EventDragnDrop;
 
 /**
  * WIN32 Implementation of GHOST_System class.
@@ -181,6 +182,18 @@ public:
         * @return                              No return
         */
        virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const;
+       
+       /**
+        * Creates a drag'n'drop event and pushes it immediately onto the event queue. 
+        * Called by GHOST_DropTargetWin32 class.
+        * @param eventType The type of drag'n'drop event
+        * @param draggedObjectType The type object concerned (currently array of file names, string, ?bitmap)
+        * @param mouseX x mouse coordinate (in window coordinates)
+        * @param mouseY y mouse coordinate
+        * @param window The window on which the event occured
+        * @return Indication whether the event was handled. 
+        */
+       static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,GHOST_IWindow* window, int mouseX, int mouseY, void* data);
         
 protected:
        /**
@@ -228,7 +241,7 @@ protected:
         * @param window        The window receiving the event (the active window).
         * @return The event created.
         */
-       static GHOST_EventCursor* processCursorEvent(GHOST_TEventType type, GHOST_IWindow *window);
+       static GHOST_EventCursor* processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow);
 
        /**
         * Creates a mouse wheel event.
@@ -255,7 +268,12 @@ protected:
         * @return The event created.
         */
        static GHOST_Event* processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window);
-
+       /** 
+        * Handles minimum window size.
+        * @param minmax        The MINMAXINFO structure.
+        */
+       static void processMinMaxInfo(MINMAXINFO * minmax);
+       
        /**
         * Returns the local state of the modifier keys (from the message queue).
         * @param keys The state of the keys.
index e2caf31edee9fcc2d0edda0ce174daddd228dec7..ea14f1dfc1b4dd18a5aa606fcb6fdcb82f026b1f 100644 (file)
@@ -40,6 +40,8 @@
 
 #include <string.h>
 #include "GHOST_WindowWin32.h"
+#include "GHOST_SystemWin32.h"
+#include "GHOST_DropTargetWin32.h"
 #include <GL/gl.h>
 #include <math.h>
 
@@ -95,6 +97,7 @@ static PIXELFORMATDESCRIPTOR sPreferredFormat = {
 };
 
 GHOST_WindowWin32::GHOST_WindowWin32(
+       GHOST_SystemWin32 * system,
        const STR_String& title,
        GHOST_TInt32 left,
        GHOST_TInt32 top,
@@ -106,6 +109,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(
 :
        GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone,
        stereoVisual),
+       m_system(system),
        m_hDC(0),
        m_hGlRc(0),
        m_hasMouseCaptured(false),
@@ -167,6 +171,9 @@ GHOST_WindowWin32::GHOST_WindowWin32(
                        0);                                                     // pointer to window-creation data
        }
        if (m_hWnd) {
+               // Register this window as a droptarget. Requires m_hWnd to be valid.
+               // Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
+               m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
                // Store a pointer to this class in the window structure
                ::SetWindowLongPtr(m_hWnd, GWL_USERDATA, (LONG_PTR)this);
 
@@ -275,6 +282,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
                m_hDC = 0;
        }
        if (m_hWnd) {
+               m_dropTarget->Release(); // frees itself.
                ::DestroyWindow(m_hWnd);
                m_hWnd = 0;
        }
@@ -285,6 +293,10 @@ bool GHOST_WindowWin32::getValid() const
        return m_hWnd != 0;
 }
 
+HWND GHOST_WindowWin32::getHWND() const
+{
+       return m_hWnd;
+}
 
 void GHOST_WindowWin32::setTitle(const STR_String& title)
 {
@@ -663,6 +675,41 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
        return GHOST_kSuccess;
 }
 
+GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
+{
+       if(mode != GHOST_kGrabDisable) {
+               if(mode != GHOST_kGrabNormal) {
+                       m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
+                       setCursorGrabAccum(0, 0);
+
+                       if(mode == GHOST_kGrabHide)
+                               setWindowCursorVisibility(false);
+               }
+               registerMouseClickEvent(true);
+       }
+       else {
+               if (m_cursorGrab==GHOST_kGrabHide) {
+                       m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
+                       setWindowCursorVisibility(true);
+               }
+               if(m_cursorGrab != GHOST_kGrabNormal) {
+                       /* use to generate a mouse move event, otherwise the last event
+                        * blender gets can be outside the screen causing menus not to show
+                        * properly unless the user moves the mouse */
+                        GHOST_TInt32 pos[2];
+                        m_system->getCursorPosition(pos[0], pos[1]);
+                        m_system->setCursorPosition(pos[0], pos[1]);
+               }
+
+               /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
+               setCursorGrabAccum(0, 0);
+               m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
+               registerMouseClickEvent(false);
+       }
+       
+       return GHOST_kSuccess;
+}
+
 GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
 {
        if (m_customCursor) {
@@ -676,6 +723,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cur
 
        return GHOST_kSuccess;
 }
+
 void GHOST_WindowWin32::processWin32TabletInitEvent()
 {
        if (m_wintab) {
index 8b461802fa45c27a97e56f1dc99ab10b81117027..07a31911a5e28a21d7d7b33fb4f17a8db070bb59 100644 (file)
@@ -47,6 +47,9 @@
 #define PACKETMODE     PK_BUTTONS
 #include <pktdef.h>
 
+class GHOST_SystemWin32;
+class GHOST_DropTargetWin32;
+
 // typedefs for WinTab functions to allow dynamic loading
 typedef UINT (API * GHOST_WIN32_WTInfo) ( UINT, UINT, LPVOID );
 typedef HCTX (API * GHOST_WIN32_WTOpen) (HWND, LPLOGCONTEXTA, BOOL);
@@ -74,6 +77,7 @@ public:
         * @param stereoVisual  Stereo visual for quad buffered stereo.
         */
        GHOST_WindowWin32(
+               GHOST_SystemWin32 * system,
                const STR_String& title,
                GHOST_TInt32 left,
                GHOST_TInt32 top,
@@ -96,6 +100,12 @@ public:
         */
        virtual bool getValid() const;
 
+       /**
+        * Access to the handle of the window.
+        * @return The handle of the window.
+        */
+       virtual HWND getHWND() const;
+
        /**
         * Sets the title displayed in the title bar.
         * @param title The title to display in the title bar.
@@ -250,6 +260,13 @@ protected:
         */
        virtual GHOST_TSuccess setWindowCursorVisibility(bool visible);
        
+       /**
+        * Sets the cursor grab on the window using native window system calls.
+        * Using registerMouseClickEvent.
+        * @param mode  GHOST_TGrabCursorMode.
+        */
+       virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode);
+       
        /**
         * Sets the cursor shape on the window using
         * native window system calls.
@@ -273,6 +290,10 @@ protected:
                int bg_color
        );
        
+       /** Pointer to system */
+       GHOST_SystemWin32 * m_system;
+       /** Pointer to COM IDropTarget implementor */
+       GHOST_DropTargetWin32 * m_dropTarget;
        /** Window handle. */
        HWND m_hWnd;
        /** Device context handle. */
index 6145871f9d40b4183cb7e10631b6968f61f02e28..7decf7b2ed2c08d4cbf4c1380c5bd5a657e1f8f9 100644 (file)
@@ -172,12 +172,13 @@ void WM_cursor_grab(wmWindow *win, int wrap, int hide, int *bounds)
 
        if(hide)                mode = GHOST_kGrabHide;
        else if(wrap)   mode = GHOST_kGrabWrap;
-
        if ((G.f & G_DEBUG) == 0) {
                if (win && win->ghostwin) {
                        const GHOST_TabletData *tabletdata= GHOST_GetTabletData(win->ghostwin);
-                       
-                       if ((tabletdata) && (tabletdata->Active == GHOST_kTabletModeNone))
+                       // Note: There is no tabletdata on Windows if no tablet device is connected.
+                       if (!tabletdata)
+                               GHOST_SetCursorGrab(win->ghostwin, mode, bounds);
+                       else if (tabletdata->Active == GHOST_kTabletModeNone)
                                GHOST_SetCursorGrab(win->ghostwin, mode, bounds);
                }
        }
index e34ee05fbe7bc6bc56fbb751f9431642c391dd02..f010a82fd6abad1d199101a29746eec0c5748f15 100755 (executable)
@@ -75,6 +75,7 @@ def validate_arguments(args, bc):
                        'BF_MSVS',
                        'WITH_BF_FHS',
                        'BF_VERSION',
+                       'BF_GHOST_DEBUG'
                        ]
        
        # Have options here that scons expects to be lists
@@ -418,8 +419,9 @@ def read_opts(cfg, args):
                (BoolVariable('WITH_BF_FHS', 'Use the Unix "Filesystem Hierarchy Standard" rather then a redistributable directory layout', False)),
                ('BF_VERSION', 'The root path for Unix (non-apple)', '2.5'),
 
-               (BoolVariable('BF_UNIT_TEST', 'Build with unit test support.', False))
-
+               (BoolVariable('BF_UNIT_TEST', 'Build with unit test support.', False)),
+               
+               (BoolVariable('BF_GHOST_DEBUG', 'Make GHOST print events and info to stdout. (very verbose)', False))
        ) # end of opts.AddOptions()
 
        return localopts