MouseWheel different between R20 and R21



  • Having implemented support for the mouse wheel to zoom in on a GeUserArea based plugin in R21, I was surprised that this didn't work when ported to R20.

    Apparently the GetInputState(BFM_INPUT_MOUSE, chn, mi) returns false in R20, while it returns true in R21.
    Am I missing something obvious?

    Below is the full plugin code (except the necessary icon file):

    Main.cpp

    // Main.cpp
    
    #include "c4d.h"
    #include "MyUserArea.h"
    
    Bool PluginStart(void)
    {
    	ApplicationOutput("Test"_s);
    
    	if (!RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Test Cmd-Dlg-UA"_s, 0, AutoBitmap("CmdDlgUA.png"_s), "Test"_s, NewObjClear(MyCommand)))
    		ApplicationOutput("Test - failed registering CommandData");
    
    	return true;
    }
    void PluginEnd(void)
    {
    }
    Bool PluginMessage(Int32 id, void * data)
    {
    	switch (id) {
    	case C4DPL_INIT_SYS:
    		if (!g_resource.Init())
    			return false;
    		return true;
    	case C4DMSG_PRIORITY:
    		return true;
    	case C4DPL_BUILDMENU:
    		break;
    	case C4DPL_ENDACTIVITY:
    		return true;
    	}
    	return false;
    }
    

    MyUserArea.cpp

    // MyUserArea.cpp
    
    #include "c4d.h"
    #include "lib_clipmap.h"
    
    #include "MyUserArea.h"
    
    // ====================================
    // GeUserArea
    // ====================================
    
    
    MyUserArea* g_UserArea = nullptr;
    
    MyUserArea::MyUserArea(void) : mWidth(0), mHeight(0) {}
    MyUserArea::~MyUserArea(void) {}
    
    Bool MyUserArea::Init(void)
    {
    	return true;
    }
    
    Bool MyUserArea::GetMinSize(Int32& w, Int32& h)
    {
    	w = 100;
    	h = 100;
    	return true;
    }
    
    void MyUserArea::Sized(Int32 w, Int32 h)
    {
    	mWidth = w;
    	mHeight = h;
    }
    
    Bool MyUserArea::InitValues(void)
    {
    	mWidth = GetWidth();
    	mHeight = GetHeight();
    	return true;
    }
    
    void MyUserArea::DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg)
    {
    	OffScreenOn();
    	const Int32 w = GetWidth();
    	const Int32 h = GetHeight();
    	const Int32 size = (w < h) ? w : h;
    	const Int32 halfsize = size / 2;
    
    	if (!mClipmap)
    		return;
    	mClipmap->Init(w, h, 32);
    	mClipmap->BeginDraw();
    
    	Vector color(64);
    	mClipmap->SetColor(SAFEINT32(color.x), SAFEINT32(color.y), SAFEINT32(color.z), 255);
    	mClipmap->FillRect(0, 0, w, h);
    
    	// do some more drawing stuff ...
    
    	mClipmap->EndDraw();
    	DrawBitmap(mClipmap->GetBitmap(), 0, 0, w, h, 0, 0, w, h, BMP_ALLOWALPHA);
    }
    
    Bool MyUserArea::InputEvent(const BaseContainer& msg)
    {
    	const Int32 dev = msg.GetInt32(BFM_INPUT_DEVICE);
    	const Int32 chn = msg.GetInt32(BFM_INPUT_CHANNEL);
    
    	if (dev == BFM_INPUT_MOUSE)
    	{
    		if (chn == BFM_INPUT_MOUSEWHEEL)
    		{
    			const Float wheel = msg.GetFloat(BFM_INPUT_VSCROLL) / 120.0;
    
    			BaseContainer mi;
    			if (!GetInputState(BFM_INPUT_MOUSE, chn, mi))  // *** here seems to be the difference between R20 and R21 ***
    			{
    				ApplicationOutput("MouseWheel - no input state");
    				return false;
    			}
    			Int32 mx = mi.GetInt32(BFM_INPUT_X);
    			Int32 my = mi.GetInt32(BFM_INPUT_Y);
    			Global2Local(&mx, &my);
    
    			ApplicationOutput("Mousewheel value @ at x=@, y=@", wheel, mx, my);
    			return true;
    		}
    	}
    
    	return false;
    }
    
    // ====================================
    // GeDialog
    // ====================================
    
    MyDialog::MyDialog(void) {}
    MyDialog::~MyDialog(void) {}
    
    Bool MyDialog::CreateLayout(void)
    {
    	Bool res = GeDialog::CreateLayout();
    
    	GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, ""_s, 0);
    	GroupBorderSpace(4, 4, 4, 4);
    
    	// userarea for drawing
    	GroupBegin(0, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, String(), 0);
    	C4DGadget* ua = AddUserArea(IDC_USERAREA, BFH_SCALEFIT | BFV_SCALEFIT, 400, 400);
    	if (ua)
    		AttachUserArea(mUserArea, IDC_USERAREA);
    	GroupEnd();
    
    	GroupEnd();
    
    	return res;
    }
    
    Bool MyDialog::InitValues(void)
    {
    	if (!GeDialog::InitValues())
    		return false;
    
    	return true;
    }
    
    void MyDialog::DestroyWindow(void)
    {
    	if (g_UserArea)
    		g_UserArea = nullptr;
    }
    
    // ====================================
    // CommandData
    // ====================================
    
    Bool MyCommand::Execute(BaseDocument* doc, GeDialog* parentManager)
    {
    	if (!dlg.IsOpen())
    	{
    		dlg.Open(DLG_TYPE::ASYNC, MYCOMMAND_PLUGIN_ID, -1, -1, 0, 0, 0);
    		g_UserArea = &dlg.mUserArea;
    	}
    	else
    	{
    		if (g_UserArea)
    			g_UserArea->Redraw();
    	}
    
    	return true;
    }
    
    Bool MyCommand::RestoreLayout(void* secret)
    {
    	Int32 subid = ((RestoreLayoutSecret*)secret)->subid;
    	if (subid == 0)
    	{
    		Bool ret = dlg.RestoreLayout(MYCOMMAND_PLUGIN_ID, subid, secret);
    		g_UserArea = &dlg.mUserArea;
    		return ret;
    	}
    	return false;
    }
    

    MyUserArea.h

    // MyUserArea.h
    
    #include "c4d.h"
    #include "lib_clipmap.h"
    #include "PluginIDs.h"
    
    // ====================================
    // GeUserArea
    // ====================================
    
    class MyUserArea : public GeUserArea
    {
    	INSTANCEOF(MyUserArea, GeUserArea)
    
    public:
    	MyUserArea(void);
    	virtual ~MyUserArea();
    
    	virtual Bool Init(void);
    	virtual Bool GetMinSize(Int32& w, Int32& h);
    	virtual void Sized(Int32 w, Int32 h);
    	virtual Bool InitValues(void);
    	virtual void DrawMsg(Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer& msg);
    	virtual Bool InputEvent(const BaseContainer& msg);
    
    private:
    	Int32 mWidth;
    	Int32 mHeight;
    
    	AutoAlloc<GeClipMap> mClipmap;
    };
    
    // ====================================
    // GeDialog
    // ====================================
    
    class MyDialog : public GeDialog
    {
    public:
    	MyDialog(void);
    	virtual ~MyDialog(void);
    	virtual Bool CreateLayout(void);
    	virtual Bool InitValues(void);
    	virtual void DestroyWindow(void);
    
    	MyUserArea	mUserArea;
    };
    
    // ====================================
    // CommandData
    // ====================================
    
    class MyCommand : public CommandData
    {
    	INSTANCEOF(MyCommand, CommandData)
    
    public
    	virtual Bool Execute(BaseDocument* doc, GeDialog* parentManager);
    	virtual Bool RestoreLayout(void* secret);
    
    	MyDialog dlg;
    };
    

    PluginIDs.h

    // PluginIDs.h
    
    // Dummy IDs - for demonstration purposes only
    #define MYCOMMAND_PLUGIN_ID			1000000
    
    // gadget IDs
    #define IDC_USERAREA		50000
    
    

    Note that for R20 one need to remove the 2nd parameter of the CommandData::Execute method!
    I am using R20.059 and R21.207



  • Hi @C4DS, thanks for reaching out us.

    There's actually nothing weird in your code rather than you're just attempting to back-port a code designed to work from R21 onward to R20.

    Please have a look at our asynctest example on GitHub on how it was handled in R20.

    Best, R



  • @r_gigante
    Well ... this is embarrassing.

    I came up with the R21 code using notes I had collected over the years, and was rather confused seeing this didn't port over to R20.
    Actually the correct code should look like following:

    Bool MyUserArea::InputEvent(const BaseContainer& msg)
    {
    	const Int32 dev = msg.GetInt32(BFM_INPUT_DEVICE);
    	const Int32 chn = msg.GetInt32(BFM_INPUT_CHANNEL);
    
    	if (dev == BFM_INPUT_MOUSE)
    	{
    		if (chn == BFM_INPUT_MOUSEWHEEL)
    		{
    			const Float wheel = msg.GetFloat(BFM_INPUT_VSCROLL) / 120.0;
    			Int32 mx = msg.GetInt32(BFM_INPUT_X);
    			Int32 my = msg.GetInt32(BFM_INPUT_Y);
    			Global2Local(&mx, &my);
    
    			ApplicationOutput("Mousewheel value @ at x=@, y=@", wheel, mx, my);
    			return true;
    		}
    	}
    
    	return false;
    }
    

    No need at all to perform a GetInputState to obtain the the x and y coordinates from, as the BaseContainer to get it from is already provided as "msg". And this works both for R21 and R20.

    Man ... sometimes you think you know what you're doing, and then it seems you just know squad. Embarrassing!

    Riccardo, terribly sorry for wasting your time!