From 5a3791ab5b2274fffde961bd01c75bacee74f634 Mon Sep 17 00:00:00 2001 From: Nathan Letwory Date: Wed, 2 Dec 2009 00:57:12 +0000 Subject: [PATCH] Apply patch [#20145] Ghost Win32 roundup patch: Minimum Window Size, Continuous Grab and Drag And Drop 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.! --- config/win32-mingw-config.py | 2 +- intern/ghost/SConscript | 6 +- intern/ghost/intern/GHOST_Debug.h | 7 +- intern/ghost/intern/GHOST_DropTargetWin32.cpp | 426 ++++++++++++++++++ intern/ghost/intern/GHOST_DropTargetWin32.h | 155 +++++++ intern/ghost/intern/GHOST_EventPrinter.cpp | 13 +- intern/ghost/intern/GHOST_SystemWin32.cpp | 100 +++- intern/ghost/intern/GHOST_SystemWin32.h | 22 +- intern/ghost/intern/GHOST_WindowWin32.cpp | 48 ++ intern/ghost/intern/GHOST_WindowWin32.h | 21 + .../blender/windowmanager/intern/wm_cursors.c | 7 +- tools/btools.py | 6 +- 12 files changed, 788 insertions(+), 25 deletions(-) create mode 100644 intern/ghost/intern/GHOST_DropTargetWin32.cpp create mode 100644 intern/ghost/intern/GHOST_DropTargetWin32.h diff --git a/config/win32-mingw-config.py b/config/win32-mingw-config.py index 709261ec1ca..0f07ca4c2ee 100644 --- a/config/win32-mingw-config.py +++ b/config/win32-mingw-config.py @@ -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'] diff --git a/intern/ghost/SConscript b/intern/ghost/SConscript index 09da6f94ddc..84ca97fdb93 100644 --- a/intern/ghost/SConscript +++ b/intern/ghost/SConscript @@ -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] ) diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h index 7f836202720..1ca4ce2b6de 100644 --- a/intern/ghost/intern/GHOST_Debug.h +++ b/intern/ghost/intern/GHOST_Debug.h @@ -37,12 +37,17 @@ #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 + #include //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 index 00000000000..d75770383fb --- /dev/null +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cpp @@ -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\n%s\n\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 index 00000000000..6ea6ed85a93 --- /dev/null +++ b/intern/ghost/intern/GHOST_DropTargetWin32.h @@ -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 +#include +#include +#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_ diff --git a/intern/ghost/intern/GHOST_EventPrinter.cpp b/intern/ghost/intern/GHOST_EventPrinter.cpp index c6b3416669e..91b55474441 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.cpp +++ b/intern/ghost/intern/GHOST_EventPrinter.cpp @@ -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;icount;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; } diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 8a17d455695..15914a5bf43 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -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 diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 6a12975a3dd..517f7ddbeea 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -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. diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index e2caf31edee..ea14f1dfc1b 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -40,6 +40,8 @@ #include #include "GHOST_WindowWin32.h" +#include "GHOST_SystemWin32.h" +#include "GHOST_DropTargetWin32.h" #include #include @@ -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) { diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index 8b461802fa4..07a31911a5e 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -47,6 +47,9 @@ #define PACKETMODE PK_BUTTONS #include +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. */ diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 6145871f9d4..7decf7b2ed2 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -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); } } diff --git a/tools/btools.py b/tools/btools.py index e34ee05fbe7..f010a82fd6a 100755 --- a/tools/btools.py +++ b/tools/btools.py @@ -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