Adjustments to continuous grab

- Use an enum for grab modes rather then boolean options.
 -- GHOST_kGrabNormal: continuous grab userpref disabled
 -- GHOST_kGrabWrap: wrap the mouse at the screen bounds *
 -- GHOST_kGrabHide: hide the mouse while grabbing and restore the mouse where it was initially pressed *

GrabWrap is nice for transform and tools where you want some idea where the cursor is, previously I found both restoring the mouse at its original location and restoring at a clamped location was confusing with operators like transform, wrapping is not ideal but IMHO the best of a bad bunch of options.
GrabHide  is for numbuts, where restoring the mouse at the initial location isnt so confusing.
This commit is contained in:
Campbell Barton 2009-10-17 14:08:01 +00:00
parent 53624a53d9
commit 91d89c1ff7
15 changed files with 114 additions and 117 deletions

@ -376,7 +376,7 @@ extern GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
* @return Indication of success. * @return Indication of success.
*/ */
extern GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, extern GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
int grab, int warp, int restore); GHOST_TGrabCursorMode mode);
/*************************************************************************************** /***************************************************************************************
** Access to mouse button and keyboard states. ** Access to mouse button and keyboard states.

@ -271,7 +271,7 @@ public:
* @param grab The new grab state of the cursor. * @param grab The new grab state of the cursor.
* @return Indication of success. * @return Indication of success.
*/ */
virtual GHOST_TSuccess setCursorGrab(bool grab, bool warp, bool restore) { return GHOST_kSuccess; }; virtual GHOST_TSuccess setCursorGrab(GHOST_TGrabCursorMode mode) { return GHOST_kSuccess; };
}; };

@ -126,6 +126,13 @@ public:
*/ */
virtual inline void unionPoint(GHOST_TInt32 x, GHOST_TInt32 y); virtual inline void unionPoint(GHOST_TInt32 x, GHOST_TInt32 y);
/**
* Grows the rectangle to included a point.
* @param x The x-coordinate of the point.
* @param y The y-coordinate of the point.
*/
virtual inline void wrapPoint(GHOST_TInt32 &x, GHOST_TInt32 &y, GHOST_TInt32 ofs);
/** /**
* Returns whether the point is inside this rectangle. * Returns whether the point is inside this rectangle.
* Point on the boundary is considered inside. * Point on the boundary is considered inside.
@ -222,6 +229,21 @@ inline void GHOST_Rect::unionPoint(GHOST_TInt32 x, GHOST_TInt32 y)
if (y > m_b) m_b = y; if (y > m_b) m_b = y;
} }
inline void GHOST_Rect::wrapPoint(GHOST_TInt32 &x, GHOST_TInt32 &y, GHOST_TInt32 ofs)
{
GHOST_TInt32 w= getWidth();
GHOST_TInt32 h= getHeight();
/* highly unlikely but avoid eternal loop */
if(w-ofs <= 0 || h-ofs <= 0)
return;
while(x-ofs < m_l) x+= w;
while(y-ofs < m_t) y+= h;
while(x+ofs > m_r) x-= w;
while(y+ofs > m_b) y-= h;
}
inline bool GHOST_Rect::isInside(GHOST_TInt32 x, GHOST_TInt32 y) const inline bool GHOST_Rect::isInside(GHOST_TInt32 x, GHOST_TInt32 y) const
{ {
return (x >= m_l) && (x <= m_r) && (y >= m_t) && (y <= m_b); return (x >= m_l) && (x <= m_r) && (y >= m_t) && (y <= m_b);

@ -341,6 +341,12 @@ typedef enum {
GHOST_kKeyF24 GHOST_kKeyF24
} GHOST_TKey; } GHOST_TKey;
typedef enum {
GHOST_kGrabDisable = 0, /* grab not set */
GHOST_kGrabNormal, /* no cursor adjustments */
GHOST_kGrabWrap, /* wrap the mouse location to prevent limiting screen bounds */
GHOST_kGrabHide, /* hide the mouse while grabbing and restore the original location on release (numbuts) */
} GHOST_TGrabCursorMode;
typedef void* GHOST_TEventDataPtr; typedef void* GHOST_TEventDataPtr;

@ -355,11 +355,11 @@ GHOST_TSuccess GHOST_SetCursorPosition(GHOST_SystemHandle systemhandle,
GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle, GHOST_TSuccess GHOST_SetCursorGrab(GHOST_WindowHandle windowhandle,
int grab, int warp, int restore) GHOST_TGrabCursorMode mode)
{ {
GHOST_IWindow* window = (GHOST_IWindow*) windowhandle; GHOST_IWindow* window = (GHOST_IWindow*) windowhandle;
return window->setCursorGrab(grab?true:false, warp?true:false, restore?true:false); return window->setCursorGrab(mode);
} }

@ -388,32 +388,37 @@ GHOST_SystemX11::processEvent(XEvent *xe)
{ {
XMotionEvent &xme = xe->xmotion; XMotionEvent &xme = xe->xmotion;
if(window->getCursorWarp()) { if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
/* Calculate offscreen location and re-center the mouse */ {
GHOST_TInt32 x_warp, y_warp, x_new, y_new, x_accum, y_accum; GHOST_TInt32 x_new= xme.x_root;
GHOST_TInt32 y_new= xme.y_root;
GHOST_TInt32 x_accum, y_accum;
GHOST_Rect bounds;
window->getCursorWarpPos(x_warp, y_warp); window->getClientBounds(bounds);
getCursorPosition(x_new, y_new);
if(x_warp != x_new || y_warp != y_new) { /* could also clamp to screen bounds
window->getCursorWarpAccum(x_accum, y_accum); * wrap with a window outside the view will fail atm */
x_accum += x_new - x_warp;
y_accum += y_new - y_warp;
window->setCursorWarpAccum(x_accum, y_accum); bounds.wrapPoint(x_new, y_new, 1); /* offset of one incase blender is at screen bounds */
setCursorPosition(x_warp, y_warp); /* reset */
window->getCursorGrabAccum(x_accum, y_accum);
if(x_new != xme.x_root || y_new != xme.y_root) {
setCursorPosition(x_new, y_new); /* wrap */
window->setCursorGrabAccum(x_accum + (xme.x_root - x_new), y_accum + (xme.y_root - y_new));
}
g_event = new g_event = new
GHOST_EventCursor( GHOST_EventCursor(
getMilliSeconds(), getMilliSeconds(),
GHOST_kEventCursorMove, GHOST_kEventCursorMove,
window, window,
x_warp + x_accum, xme.x_root + x_accum,
y_warp + y_accum xme.y_root + y_accum
); );
} }
}
else { else {
g_event = new g_event = new
GHOST_EventCursor( GHOST_EventCursor(

@ -48,15 +48,14 @@ GHOST_Window::GHOST_Window(
: :
m_drawingContextType(type), m_drawingContextType(type),
m_cursorVisible(true), m_cursorVisible(true),
m_cursorGrabbed(false), m_cursorGrab(GHOST_kGrabDisable),
m_cursorWarp(false),
m_cursorShape(GHOST_kStandardCursorDefault), m_cursorShape(GHOST_kStandardCursorDefault),
m_stereoVisual(stereoVisual) m_stereoVisual(stereoVisual)
{ {
m_isUnsavedChanges = false; m_isUnsavedChanges = false;
m_cursorWarpAccumPos[0] = 0; m_cursorGrabAccumPos[0] = 0;
m_cursorWarpAccumPos[1] = 0; m_cursorGrabAccumPos[1] = 0;
m_fullScreen = state == GHOST_kWindowStateFullScreen; m_fullScreen = state == GHOST_kWindowStateFullScreen;
if (m_fullScreen) { if (m_fullScreen) {
@ -98,13 +97,13 @@ GHOST_TSuccess GHOST_Window::setCursorVisibility(bool visible)
} }
} }
GHOST_TSuccess GHOST_Window::setCursorGrab(bool grab, bool warp, bool restore) GHOST_TSuccess GHOST_Window::setCursorGrab(GHOST_TGrabCursorMode mode)
{ {
if(m_cursorGrabbed == grab) if(m_cursorGrab == mode)
return GHOST_kSuccess; return GHOST_kSuccess;
if (setWindowCursorGrab(grab, warp, restore)) { if (setWindowCursorGrab(mode)) {
m_cursorGrabbed = grab; m_cursorGrab = mode;
return GHOST_kSuccess; return GHOST_kSuccess;
} }
else { else {

@ -158,10 +158,9 @@ public:
* @return The visibility state of the cursor. * @return The visibility state of the cursor.
*/ */
inline virtual bool getCursorVisibility() const; inline virtual bool getCursorVisibility() const;
inline virtual bool getCursorWarp() const; inline virtual GHOST_TGrabCursorMode getCursorGrabMode() const;
inline virtual bool getCursorWarpPos(GHOST_TInt32 &x, GHOST_TInt32 &y) const; inline virtual void getCursorGrabAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const;
inline virtual bool getCursorWarpAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const; inline virtual void setCursorGrabAccum(GHOST_TInt32 x, GHOST_TInt32 y);
inline virtual bool setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y);
/** /**
* Shows or hides the cursor. * Shows or hides the cursor.
@ -175,7 +174,7 @@ public:
* @param grab The new grab state of the cursor. * @param grab The new grab state of the cursor.
* @return Indication of success. * @return Indication of success.
*/ */
virtual GHOST_TSuccess setCursorGrab(bool grab, bool warp, bool restore); virtual GHOST_TSuccess setCursorGrab(GHOST_TGrabCursorMode mode);
/** /**
* Sets the window "modified" status, indicating unsaved changes * Sets the window "modified" status, indicating unsaved changes
@ -247,7 +246,7 @@ protected:
* Sets the cursor grab on the window using * Sets the cursor grab on the window using
* native window system calls. * native window system calls.
*/ */
virtual GHOST_TSuccess setWindowCursorGrab(bool grab, bool warp, bool restore) { return GHOST_kSuccess; }; virtual GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) { return GHOST_kSuccess; };
/** /**
* Sets the cursor shape on the window using * Sets the cursor shape on the window using
@ -274,16 +273,13 @@ protected:
bool m_cursorVisible; bool m_cursorVisible;
/** The current grabbed state of the cursor */ /** The current grabbed state of the cursor */
bool m_cursorGrabbed; GHOST_TGrabCursorMode m_cursorGrab;
/** The current warped state of the cursor */
bool m_cursorWarp;
/** Initial grab location. */ /** Initial grab location. */
GHOST_TInt32 m_cursorWarpInitPos[2]; GHOST_TInt32 m_cursorGrabInitPos[2];
/** Accumulated offset from m_cursorWarpInitPos. */ /** Accumulated offset from m_cursorGrabInitPos. */
GHOST_TInt32 m_cursorWarpAccumPos[2]; GHOST_TInt32 m_cursorGrabAccumPos[2];
/** The current shape of the cursor */ /** The current shape of the cursor */
GHOST_TStandardCursor m_cursorShape; GHOST_TStandardCursor m_cursorShape;
@ -317,40 +313,21 @@ inline bool GHOST_Window::getCursorVisibility() const
return m_cursorVisible; return m_cursorVisible;
} }
inline bool GHOST_Window::getCursorWarp() const inline GHOST_TGrabCursorMode GHOST_Window::getCursorGrabMode() const
{ {
return m_cursorWarp; return m_cursorGrab;
} }
inline bool GHOST_Window::getCursorWarpPos(GHOST_TInt32 &x, GHOST_TInt32 &y) const inline void GHOST_Window::getCursorGrabAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const
{ {
if(m_cursorWarp==false) x= m_cursorGrabAccumPos[0];
return GHOST_kFailure; y= m_cursorGrabAccumPos[1];
x= m_cursorWarpInitPos[0];
y= m_cursorWarpInitPos[1];
return GHOST_kSuccess;
} }
inline bool GHOST_Window::getCursorWarpAccum(GHOST_TInt32 &x, GHOST_TInt32 &y) const inline void GHOST_Window::setCursorGrabAccum(GHOST_TInt32 x, GHOST_TInt32 y)
{ {
if(m_cursorWarp==false) m_cursorGrabAccumPos[0]= x;
return GHOST_kFailure; m_cursorGrabAccumPos[1]= y;
x= m_cursorWarpAccumPos[0];
y= m_cursorWarpAccumPos[1];
return GHOST_kSuccess;
}
inline bool GHOST_Window::setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y)
{
if(m_cursorWarp==false)
return GHOST_kFailure;
m_cursorWarpAccumPos[0]= x;
m_cursorWarpAccumPos[1]= y;
return GHOST_kSuccess;
} }
inline GHOST_TStandardCursor GHOST_Window::getCursorShape() const inline GHOST_TStandardCursor GHOST_Window::getCursorShape() const

@ -239,7 +239,7 @@ protected:
/** /**
* Sets the cursor warp accumulator. Overriden for workaround due to Cocoa next event after cursor set giving delta values non zero * Sets the cursor warp accumulator. Overriden for workaround due to Cocoa next event after cursor set giving delta values non zero
*/ */
inline virtual bool setCursorWarpAccum(GHOST_TInt32 x, GHOST_TInt32 y); inline virtual bool setCursorGrabAccum(GHOST_TInt32 x, GHOST_TInt32 y);
/** /**
* Sets the cursor grab on the window using * Sets the cursor grab on the window using

@ -1400,47 +1400,27 @@ setWindowCursorVisibility(
GHOST_TSuccess GHOST_TSuccess
GHOST_WindowX11:: GHOST_WindowX11::
setWindowCursorGrab( setWindowCursorGrab(
bool grab, bool warp, bool restore GHOST_TGrabCursorMode mode
){ ){
if(grab) { if(mode != GHOST_kGrabDisable) {
if(warp) { if(mode != GHOST_kGrabNormal) {
m_system->getCursorPosition(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]); m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setCursorGrabAccum(0, 0);
setCursorWarpAccum(0, 0); if(mode == GHOST_kGrabHide)
setWindowCursorVisibility(false); setWindowCursorVisibility(false);
m_cursorWarp= true;
} }
XGrabPointer(m_display, m_window, True, ButtonPressMask| ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); XGrabPointer(m_display, m_window, True, ButtonPressMask| ButtonReleaseMask|PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
} }
else { else {
if(m_cursorWarp) { /* are we exiting warp */ if (m_cursorGrab==GHOST_kGrabHide) {
m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setWindowCursorVisibility(true); setWindowCursorVisibility(true);
}
/* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */ /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
if(restore) { setCursorGrabAccum(0, 0);
GHOST_Rect bounds;
GHOST_TInt32 x_new, y_new, x_rel, y_rel;
getClientBounds(bounds);
x_new= m_cursorWarpInitPos[0]+m_cursorWarpAccumPos[0];
y_new= m_cursorWarpInitPos[1]+m_cursorWarpAccumPos[1];
screenToClient(x_new, y_new, x_rel, y_rel);
if(x_rel < 0) x_new = (x_new-x_rel) + 2;
if(y_rel < 0) y_new = (y_new-y_rel) + 2;
if(x_rel > bounds.getWidth()) x_new -= (x_rel-bounds.getWidth()) + 2;
if(y_rel > bounds.getHeight()) y_new -= (y_rel-bounds.getHeight()) + 2;
m_system->setCursorPosition(x_new, y_new);
}
else {
m_system->setCursorPosition(m_cursorWarpInitPos[0], m_cursorWarpInitPos[1]);
}
setCursorWarpAccum(0, 0);
m_cursorWarp= false;
}
XUngrabPointer(m_display, CurrentTime); XUngrabPointer(m_display, CurrentTime);
} }

@ -256,9 +256,12 @@ protected:
*/ */
GHOST_TSuccess GHOST_TSuccess
setWindowCursorGrab( setWindowCursorGrab(
bool grab, bool warp, bool restore GHOST_TGrabCursorMode mode
); );
GHOST_TGrabCursorMode
getWindowCursorGrab() const;
/** /**
* Sets the cursor shape on the window using * Sets the cursor shape on the window using
* native window system calls. * native window system calls.

@ -3723,12 +3723,12 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
/* number editing */ /* number editing */
if(state == BUTTON_STATE_NUM_EDITING) { if(state == BUTTON_STATE_NUM_EDITING) {
if(ui_is_a_warp_but(but)) if(ui_is_a_warp_but(but))
WM_cursor_grab(CTX_wm_window(C), TRUE); WM_cursor_grab(CTX_wm_window(C), TRUE, TRUE);
ui_numedit_begin(but, data); ui_numedit_begin(but, data);
} else if(data->state == BUTTON_STATE_NUM_EDITING) { } else if(data->state == BUTTON_STATE_NUM_EDITING) {
ui_numedit_end(but, data); ui_numedit_end(but, data);
if(ui_is_a_warp_but(but)) if(ui_is_a_warp_but(but))
WM_cursor_ungrab(CTX_wm_window(C), FALSE); WM_cursor_ungrab(CTX_wm_window(C));
} }
/* menu open */ /* menu open */
if(state == BUTTON_STATE_MENU_OPEN) if(state == BUTTON_STATE_MENU_OPEN)

@ -76,8 +76,8 @@ void WM_cursor_set (struct wmWindow *win, int curs);
void WM_cursor_modal (struct wmWindow *win, int curs); void WM_cursor_modal (struct wmWindow *win, int curs);
void WM_cursor_restore (struct wmWindow *win); void WM_cursor_restore (struct wmWindow *win);
void WM_cursor_wait (int val); void WM_cursor_wait (int val);
void WM_cursor_grab(struct wmWindow *win, int warp); void WM_cursor_grab(struct wmWindow *win, int wrap, int hide);
void WM_cursor_ungrab(wmWindow *win, int restore); void WM_cursor_ungrab(struct wmWindow *win);
void WM_timecursor (struct wmWindow *win, int nr); void WM_timecursor (struct wmWindow *win, int nr);
void *WM_paint_cursor_activate(struct wmWindowManager *wm, int (*poll)(struct bContext *C), void (*draw)(struct bContext *C, int, int, void *customdata), void *customdata); void *WM_paint_cursor_activate(struct wmWindowManager *wm, int (*poll)(struct bContext *C), void (*draw)(struct bContext *C, int, int, void *customdata), void *customdata);

@ -163,21 +163,26 @@ void WM_cursor_wait(int val)
} }
} }
void WM_cursor_grab(wmWindow *win, int warp) void WM_cursor_grab(wmWindow *win, int wrap, int hide)
{ {
/* Only grab cursor when not running debug. /* Only grab cursor when not running debug.
* It helps not to get a stuck WM when hitting a breakpoint * It helps not to get a stuck WM when hitting a breakpoint
* */ * */
GHOST_TGrabCursorMode mode = GHOST_kGrabNormal;
if(hide) mode = GHOST_kGrabHide;
else if(wrap) mode = GHOST_kGrabWrap;
if ((G.f & G_DEBUG) == 0) if ((G.f & G_DEBUG) == 0)
if(win) if(win)
GHOST_SetCursorGrab(win->ghostwin, 1, warp, -1); GHOST_SetCursorGrab(win->ghostwin, mode);
} }
void WM_cursor_ungrab(wmWindow *win, int restore) void WM_cursor_ungrab(wmWindow *win)
{ {
if ((G.f & G_DEBUG) == 0) if ((G.f & G_DEBUG) == 0)
if(win) if(win)
GHOST_SetCursorGrab(win->ghostwin, 0, -1, restore); GHOST_SetCursorGrab(win->ghostwin, GHOST_kGrabDisable);
} }
/* afer this you can call restore too */ /* afer this you can call restore too */

@ -464,7 +464,7 @@ static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, P
/* grab cursor during blocking modal ops (X11) */ /* grab cursor during blocking modal ops (X11) */
if(ot->flag & OPTYPE_BLOCKING) { if(ot->flag & OPTYPE_BLOCKING) {
int warp = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER)); int warp = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
WM_cursor_grab(CTX_wm_window(C), warp); WM_cursor_grab(CTX_wm_window(C), warp, FALSE);
} }
} }
else else
@ -660,7 +660,7 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers)
CTX_wm_region_set(C, region); CTX_wm_region_set(C, region);
} }
WM_cursor_ungrab(CTX_wm_window(C), TRUE); WM_cursor_ungrab(CTX_wm_window(C));
WM_operator_free(handler->op); WM_operator_free(handler->op);
} }
else if(handler->ui_remove) { else if(handler->ui_remove) {
@ -858,7 +858,7 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
/* remove modal handler, operator itself should have been cancelled and freed */ /* remove modal handler, operator itself should have been cancelled and freed */
if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) { if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
WM_cursor_ungrab(CTX_wm_window(C), TRUE); WM_cursor_ungrab(CTX_wm_window(C));
BLI_remlink(handlers, handler); BLI_remlink(handlers, handler);
wm_event_free_handler(handler); wm_event_free_handler(handler);