Getting windows mousepointer icon = black square Topic is solved

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Fri Apr 16, 2021 3:18 pm

Parduz wrote:
Fri Apr 16, 2021 3:16 pm
Anyway, i still wonder WHY we should use the wxIcon, and then the wxBitmap, while the IconInfo.hbmColor and hbmMask already are the HBITMAP we need.
You cans use them but for monochrome cursors, you need to do what wxWidgets attempts to do, i.e., properly blend the two parts of the mask...

User avatar
Parduz
Earned some good credits
Earned some good credits
Posts: 118
Joined: Fri Jan 30, 2015 1:48 pm
Location: Bologna, Italy

Re: Getting windows mousepointer icon = black square

Post by Parduz » Fri Apr 16, 2021 3:20 pm

PB wrote:
Fri Apr 16, 2021 3:18 pm
Parduz wrote:
Fri Apr 16, 2021 3:16 pm
Anyway, i still wonder WHY we should use the wxIcon, and then the wxBitmap, while the IconInfo.hbmColor and hbmMask already are the HBITMAP we need.
You cans use them but for monochrome cursors, you need to do what wxWidgets attempts to do, i.e., properly blend the two parts of the mask...
I'd like to do it so much, but until i can't get anything even from the "normal" hbmColor i'm stuck.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Fri Apr 16, 2021 3:22 pm

Parduz wrote:
Fri Apr 16, 2021 3:20 pm
I'd like to do it so much, but until i can't get anything even from the "normal" hbmColor i'm stuck.
As shown in my code from my previous post, hbmColor is not used for monochrome cursors, so...

User avatar
Parduz
Earned some good credits
Earned some good credits
Posts: 118
Joined: Fri Jan 30, 2015 1:48 pm
Location: Bologna, Italy

Re: Getting windows mousepointer icon = black square

Post by Parduz » Fri Apr 16, 2021 3:27 pm

Ok, but:

Code: Select all

	BITMAP bmMask,bmCurs;
    if ( !ii.hbmMask ) {
		wxLogMessage("NO Cursor Mask???");
		return false;
	}else{
		if( GetObject(ii.hbmMask, sizeof(bmMask), &bmMask) != sizeof(bmMask) ) {
			wxLogError("Could not get BITMAP from the mask.");
			return false;
		}
		wxLogMessage("Cursor Mask: size = %dx%d pixels, depth = %d.",
        bmMask.bmWidth, bmMask.bmHeight, bmMask.bmBitsPixel);
	}
    if ( !ii.hbmColor ) {
        wxLogMessage("The cursor is monochrome.");
    } else {
        wxLogMessage("The cursor is in color.");
		if( GetObject(ii.hbmColor, sizeof(bmCurs), &bmCurs) != sizeof(bmCurs) ) {
			wxLogError("Could not get BITMAP from the mask.");
			return false;
		}
		wxLogMessage("Cursor Mask: size = %dx%d pixels, depth = %d.",
        bmCurs.bmWidth, bmCurs.bmHeight, bmCurs.bmBitsPixel);

        if (!bitmap.InitFromHBITMAP(ii.hbmColor, bmCurs.bmWidth, bmCurs.bmHeight, bmCurs.bmBitsPixel) ) {
			wxLogError("Could not initialize Bitmap from HBITMAP.");
			return false;
        }
        wxMask bmMask(ii.hbmMask);
        bitmap.SetMask(&bmMask);
	}
This is a try to get the color one (i modified your sample):
WHY it does'nt work?

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Fri Apr 16, 2021 3:35 pm

Parduz wrote:
Fri Apr 16, 2021 3:27 pm
This is a try to get the color one (i modified your sample):
WHY it does'nt work?
I think there is a bug, you are always missing "!" when checking the result of ::GetObject(), this function returns zero on failure, non-zero otherwise. I did not read the rest of the code.

User avatar
Parduz
Earned some good credits
Earned some good credits
Posts: 118
Joined: Fri Jan 30, 2015 1:48 pm
Location: Bologna, Italy

Re: Getting windows mousepointer icon = black square

Post by Parduz » Fri Apr 16, 2021 3:45 pm

PB wrote:
Fri Apr 16, 2021 3:35 pm
Parduz wrote:
Fri Apr 16, 2021 3:27 pm
This is a try to get the color one (i modified your sample):
WHY it does'nt work?
I think there is a bug, you are always missing "!" when checking the result of ::GetObject(), this function returns zero on failure, non-zero otherwise. I did not read the rest of the code.
I check his return value against sizeof(BITMAP): whatever result (0 or not) not equal to the size of that BITMAP means a failure.
Seems to me more strict than just checking for 0.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Fri Apr 16, 2021 3:49 pm

Parduz wrote:
Fri Apr 16, 2021 3:45 pm
I check his return value against sizeof(BITMAP): whatever result (0 or not) not equal to the size of that BITMAP means a failure.
Seems to me more strict than just checking for 0.
Sorry for having missing that check, I did not look past the end of the function closing parentheses.

Anyway, the docs say
If the function succeeds, and lpvObject is a valid pointer, the return value is the number of bytes stored into the buffer.
I
I am not sure if this always means sizeof(BITMAP) and would do what the docs say, e.g., check if the result is non-zero. I have no suggestion otherwise and I am going AFK for a bit now.

BTW, IIRC from looking at the wxWidgets code I linked before, wxMask bits are different (inverted?) from Windows mask bits, so you may not be able to use HBITMAP as wxMask directly. But I a may be wrong again.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Fri Apr 16, 2021 5:39 pm

Attempting to fix wxWidgets code, I have realized that perhaps monochrome icons cannot be properly stored in a wxBitmap/wxImage.

wxBitmap stores pixels to be drawn and optionally a transparent mask to indicate which pixels to not draw. Similarly with alpha for wxBitmap/wxImage.

However, a monochrome cursor is supposed to be drawn inverted against the background, so it is always visible, e.g. black on white, white on black etc. Perhaps it could be done drawing such wxBitmap with wxDC::Blit(), using wxXOR as the ROP? But that would be a hassle, to remember which bitmap is monochrome...

So a monochrome icon has to be stored in a wxIcon and drawn with (now buggy) wxDC::DrawIcon(). I know it sounds horrible, but if it comes to worst, and if possible, you could just store the HCURSOR and draw it when needed with something like

Code: Select all

::DrawIconEx(dc.GetHDC(), x, y, hCursor0, 0, 0, nullptr, DI_NORMAL);
What do you think about this, Parduz?

BTW, we also did not speak about animated cursors but let's pretend that is a non-issue.

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Sat Apr 17, 2021 8:13 am

The issue just has been fixed in wxWidgets master: https://github.com/wxWidgets/wxWidgets/ ... 6347fef477
current-cursor.gif
current-cursor.gif (41.63 KiB) Viewed 528 times

Code: Select all

#include <wx/wx.h>
#include <wx/msw/wrapwin.h>

bool GetFromCurrentCursor(wxIcon& icon, wxBitmap& bitmap)
{
    CURSORINFO ci{0};
    wxIcon     tmpIcon;
    wxBitmap   tmpBitmap;

    ci.cbSize = sizeof(ci);
    if ( !::GetCursorInfo(&ci) || (ci.flags & CURSOR_SHOWING) == 0 )
    {
        wxLogDebug("Could not get cursor info.");
        return false;
    }

    if ( !tmpIcon.CreateFromHICON(WXHICON(ci.hCursor)) )
    {
        wxLogDebug("Could not create wxIcon from HCURSOR.");
        return false;
    }

    if ( !tmpBitmap.CopyFromIcon(tmpIcon) )
    {
        wxLogDebug("Could not create wxBitmap from wxIcon.");
        return false;
    }

    icon = tmpIcon;
    bitmap = tmpBitmap;
    return true;
}

class MyFrame: public wxFrame
{
public:
    MyFrame() : wxFrame (nullptr, wxID_ANY, "Test")
    {
        SetBackgroundColour(*wxRED);
        SetClientSize(200, 100);

        m_timer.Bind(wxEVT_TIMER,
            [this](wxTimerEvent&) { GetFromCurrentCursor(m_icon, m_bitmap); Refresh(); Update(); });
        m_timer.Start(100);

        Bind(wxEVT_PAINT, &MyFrame::OnPaint, this);
    }
private:
    wxTimer  m_timer;
    wxIcon   m_icon;
    wxBitmap m_bitmap;

    void OnPaint(wxPaintEvent&)
    {
        wxPaintDC dc(this);

        if ( !m_icon.IsOk() || !m_bitmap.IsOk() )
            return;
        dc.DrawIcon(m_icon, 20, 20);
        dc.DrawBitmap(m_bitmap, 100, 20, true);
    }
};

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        (new MyFrame())->Show();
        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
I would still recommend using wxIcon instead of wxBitmap, for reasons stated in my previous post.

User avatar
Parduz
Earned some good credits
Earned some good credits
Posts: 118
Joined: Fri Jan 30, 2015 1:48 pm
Location: Bologna, Italy

Re: Getting windows mousepointer icon = black square

Post by Parduz » Mon Apr 19, 2021 12:28 pm

PB wrote:
Sat Apr 17, 2021 8:13 am
The issue just has been fixed in wxWidgets master: https://github.com/wxWidgets/wxWidgets/ ... 6347fef477
Great!.
But....
should'nt the new code be inside the "if ( hbmp )" branch, instead of outside of it?

(just 'cause i'm gonna recompile my widgets so i want to be sure)

PB
Part Of The Furniture
Part Of The Furniture
Posts: 3001
Joined: Sun Jan 03, 2010 5:45 pm

Re: Getting windows mousepointer icon = black square

Post by PB » Mon Apr 19, 2021 12:39 pm

Parduz wrote:
Mon Apr 19, 2021 12:28 pm
But....
should'nt the new code be inside the "if ( hbmp )" branch, instead of outside of it?
Actually, looking at the code, I think it should be inside the block guarded by statement

Code: Select all

if ( ::GetObject(hbmp, sizeof(BITMAP), (LPSTR) &bm) )
However, in practice it does not matter, as when that block does not get executed, size.width is left at 0 and 0/2 is once again, 0.

Post Reply