Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added methods to set the cursor type/image #827

Merged
merged 5 commits into from Jul 18, 2017

Conversation

@binary1248
Copy link
Member

commented Mar 15, 2015

Supersedes #784.

Thanks to @fundies for the initial draft implementation. I originally wanted to attribute the commit to him, but there were simply too many changes in the end.

Resource leaks should be gone now, and general code style is fixed up. Like #784, this is still missing the OS X implementation. Hopefully @mantognini or someone else can fill in the gaps here and here. It can't be nearly as horrible as the Windows or Unix implementations. 馃槢 This might help a bit.

This implements #269.

@fundies

This comment has been minimized.

Copy link

commented Mar 15, 2015

I don't care about credit, all I wanted was an implementation. Thanks.

@LaurentGomila

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

What would be nice now is to get rid of Window::setMouseCursorVisible, and just add None to the Window::Cursor enum.

@MarioLiebisch

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

@LaurentGomila I like that idea.

But before adding/changing something twice, how about creating a sf::Cursor class rather than an enum? Cursors aren't just images, there's also a position for the clicks.

It could be expanded later on to load custom textures as hardware cursors. But on the other hand this could be a 1:1 replacement of the existing enum as well. What do you guys think?

Edit: Just seen that there is a member for loading cursor images, but I'd rather see this wrapped into their own representation similar to things like sf::Color.

@binary1248

This comment has been minimized.

Copy link
Member Author

commented Mar 15, 2015

What would be nice now is to get rid of Window::setMouseCursorVisible, and just add None to the Window::Cursor enum.

We can't get rid of it just yet, since it would break the API. We can however make it have the same function as setting the cursor to None or something other than None just for the transition phase.

But before adding/changing something twice, how about creating a sf::Cursor class rather than an enum? Cursors aren't just images, there's also a position for the clicks.

Aren't you over-engineering a bit? For us, and the operating system, the "mouse" is what is used to represent an input device. It has a position that can be queried and set. Strictly speaking, we don't even have to see anything to be able to use it. The cursor is nothing other than a sprite that just tracks the position of the mouse. Combining both input and output into the same class would be coupling functionality where it isn't really necessary.

It could be expanded later on to load custom textures as hardware cursors.

So... how is that any different to what this PR does? This PR already loads custom image data into the operating system cursor which is as "hardware" as hardware gets. There isn't anything else that is more hardware than this.

But on the other hand this could be a 1:1 replacement of the existing enum as well.

What existing enum?

@LaurentGomila

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

We can't get rid of it just yet, since it would break the API. We can however make it have the same function as setting the cursor to None or something other than None just for the transition phase.

Ah right. So either we do nothing and implement it in SFML 3, or we add None, mark setMouseCursorVisible as deprecated and forward it to setMouseCursor(None).

@MarioLiebisch

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

Combining both input and output into the same class would be coupling functionality where it isn't really necessary.

You misunderstood me. It would be just another resource, representing a cursor by holding/using a sf::Image (similar to the relation of sf::Image and sf::Texture) or a system specific constant (i.e. built-in cursors).

So... how is that any different to what this PR does?

I wrote that line before noticing that this is already in the PR, I just didn't edit it out.

What existing enum?

The one introduced by this PR.

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

@binary1248 I'll look into it hopefully in a week or two but no promise there. The implementation looks quite easy so if there's another Mac dev around, (s)he could probably do it too since the link in the description gives 75% of the implementation. 馃槈

@binary1248

This comment has been minimized.

Copy link
Member Author

commented Mar 15, 2015

What do the others think of providing a separate sf::Cursor class? It could be useful in situations where developers have to switch between cursor types very often and don't want the overhead of having to reload them every single time. Also, like @MarioLiebisch said, the system cursors can be provided as constant value sf::Cursors, although I still don't know how they would be constructed in that case without enums or some really ugly hacks.

@mantognini: 75% of the implementation for the setting the cursor to a system cursor. 馃槈 The bigger part is creating a cursor from a custom bitmap, and that part isn't really mentioned in there much.

@LaurentGomila

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

What do the others think of providing a separate sf::Cursor class? It could be useful in situations where developers have to switch between cursor types very often and don't want the overhead of having to reload them every single time.

I'd say let's start simple. It's less convenient to have to load and handle the lifetime of an additional resource, than call a simple function. We can go back to this if feedback shows that it is needed.

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

The bigger part is creating a cursor from a custom bitmap, and that part isn't really mentioned in there much.

Yes, but since setting an icon of a window already provides the code to load a bitmap and that we have -[NSCursor initWithImage:(NSImage *)newImage hotSpot:(NSPoint)aPoint], that should not be more than 25% of the total work. :-)

As for sf::Cursor class, I tend to agree with Laurent.

@fundies

This comment has been minimized.

Copy link

commented Mar 15, 2015

I started to attempt an os x implementation. Setting the cursor was fairly easy. However, I had trouble getting it to reset when the pointer was outside the window and gave up...

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 15, 2015

@fundies This is (or at least should be) already handled by the gist (indirectly) linked in the description. ;-)

@binary1248

This comment has been minimized.

Copy link
Member Author

commented Mar 15, 2015

It's less convenient to have to load and handle the lifetime of an additional resource, than call a simple function.

That is if the user really only bothers having a single cursor they never intend to change in the first place. As soon as they start interchanging cursors, they will have to manage the lifetime of their image data anyway, and having cursor objects will save them a (potentially) expensive pixel copy each time they change.

We can go back to this if feedback shows that it is needed.

The problem is that if we wait for feedback after this is in the public API, we won't be able to get rid of it that easily. 馃槈 And if we really expect people to come onto the pull request tracker to tell us what they think, well... we might end up waiting a while for opinions. 馃槢

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 17, 2015

I guess an extra level of abstraction couldn't hurt... it would also ease the creation of cursors from image/texture but more importantly be more consistent with the way we handle resources.

@LaurentGomila

This comment has been minimized.

Copy link
Member

commented Mar 17, 2015

In this case, can the one-liner call still work?

void Window::setMouseCursor(const sf::Cursor& cursor);

// with implicit constructor:
window.setMouseCursor(sf::Cursor::Arrow);

// with explicit constructor:
window.setMouseCursor(sf::Cursor(sf::Cursor::Arrow));

If this is compatible with the way internal cursor resources are managed, this could be nice to have, for all these users that won't need to optimize this bit.

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 17, 2015

I guess there would be two constructors in sf::Cursor, one implicit taking a enum and an explicit one taking an image or a bitmap. So both line would work. What do you think?

@MarioLiebisch

This comment has been minimized.

Copy link
Member

commented Mar 17, 2015

Yep, sounds good. :)

@binary1248

This comment has been minimized.

Copy link
Member Author

commented Mar 17, 2015

I guess we could make that work. XCB cursor resources get copied over to the server, so they could be destroyed directly after the call and nothing would go wrong. Windows cursors on the other hand might require a bit of reference counting in the Impl since it isn't a good idea to destroy the image store before the cursor is put out of use (haven't tried, but documentation warns about this). Don't know about the lifetime requirements on OS X though, but if it's the same as Windows, we can just use the same reference counting on all platforms.

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 17, 2015

Don't know about the lifetime requirements on OS X though, but if it's the same as Windows, we can just use the same reference counting on all platforms.

Generally speaking with OS X, the reference counter is already tangled in the API so there is not much extra work to do. Thus you don't need to worry about it when implementing the Windows part. 馃槈

@binary1248 binary1248 added this to the 2.4 milestone Mar 29, 2015

@binary1248 binary1248 removed their assignment Apr 16, 2015

@eXpl0it3r

This comment has been minimized.

Copy link
Member

commented Sep 16, 2015

What's the final verdict here? Also the branch needs rebasing.

Arrow, ///< Arrow cursor (default)
ArrowWait, ///< Busy arrow cursor
Wait, ///< Busy cursor
Text, ///< I-beam, cursor when hovering over a field allowing text entry

This comment has been minimized.

Copy link
@Bromeon

Bromeon Sep 16, 2015

Member

Is that not called "caret"? Or is caret only the blinking thing indicating where text is going to be inserted?

This comment has been minimized.

Copy link
@binary1248

binary1248 Sep 16, 2015

Author Member

the blinking thing indicating where text is going to be inserted

This.

This comment has been minimized.

Copy link
@zsbzsb

zsbzsb Sep 17, 2015

Member

I would rename 'Text' to 'IBeam' directly in the enum. It makes more sense IMO. (not sure if it helps, but it is the way the .NET framework also calls it)

This comment has been minimized.

Copy link
@CelticMinstrel

CelticMinstrel Sep 21, 2015

I personally think "Text" is fine as a name. It describes the cursor's purpose pretty well.

@Bromeon

This comment has been minimized.

Copy link
Member

commented Sep 16, 2015

Regarding the signature

void setMouseCursor(const Uint8* pixels, unsigned int width, unsigned int height,
                    Uint16 hotspotX, Uint16 hotspotY); 

Too bad this is done in the window module, otherwise we could have used

void setMouseCursor(const Image& image, Vector2u hotspot); 

But even when sticking to the Uint8 pointer, it's weird to mix unsigned int with Uint16, and unnecessary to do everything on a low level. Why not

void setMouseCursor(const Uint8* pixels, Vector2u size, Vector2u hotspot);
@LaurentGomila

This comment has been minimized.

Copy link
Member

commented Sep 17, 2015

Didn't we want to add Cursor::None and deprecate setMouseCursorVisible?

@Bromeon

This comment has been minimized.

Copy link
Member

commented Sep 17, 2015

Didn't we want to add Cursor::None and deprecate setMouseCursorVisible?

Sounds good, the question is: what would setMouseCursorVisible(true) do? I'd say setMouseCursor(Arrow) if the cursor is not visible, and do nothing otherwise.

Also, there was a discussion about a dedicated sf::Cursor class. Even if we don't implement it, I would suggest to move the sf::Window::Cursor enum out of the sf::Window class into a sf::Cursor namespace, for the following reasons:

  1. sf::Cursor::Text is much more expressive than sf::Window::Text.
  2. We don't bloat the sf::Window and sf::RenderWindow scopes with attributes not directly related to a window.
  3. If we decide to go with a dedicated cursor class later, we can simply transform the namespace sf::Cursor into a class, and code will continue to work.

@mantognini mantognini force-pushed the feature/set_cursor branch from c2b9177 to 8c31913 Mar 5, 2017

@mantognini

This comment has been minimized.

Copy link
Member

commented Mar 5, 2017

This PR was rebased onto master, the Windows implementation was rewritten based on @binary1248's previous work and the X11 implementation is new. Please do test and commend on both of those implementations as this is not my area of expertise!

You can use mantognini/SFML-Test-Events on the set_cursor branch as a basis to test those features. (The relevant bits are around line 500 of main.cpp.)

@mantognini mantognini force-pushed the feature/set_cursor branch from 8c31913 to f2187fa Mar 9, 2017

@mantognini mantognini force-pushed the feature/set_cursor branch from 1e5d1af to 9a9acf7 Mar 26, 2017

@eXpl0it3r
Copy link
Member

left a comment

This is a pure code-reading review. I'll test the implementation later on Windows.

@@ -25,6 +25,7 @@
#ifndef SFML_CONTEXTSETTINGS_HPP
#define SFML_CONTEXTSETTINGS_HPP

#include <SFML/Config.hpp>

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Is that some left-over, or why is this here?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

That might well be, yes! I'll mark this as a requested change and deal with it later.

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

Actually, now I remember: because I changed the order of some includes, this file no longer compiled (it's using Uint32). So this is actually needed and abides to our documented inclusion order.

/// \return a reference to the OS-specific implementation
///
////////////////////////////////////////////////////////////
const priv::CursorImpl& getImpl() const;

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Do we do this similarly in other classes?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

You mean deferring to an Impl class? Yes, we did it for thread, input, sensor, window, ...

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

I meant, providing an explicit getImpl() function for a friended class.

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

Ah, that's just to dispatch in one place the actual CursorImpl for the WindowImpl. I think we didn't need this before because all platform dependent types were already "inside" WindowImpl, but this is not the case here.

/// \see sf::Cursor::loadFromPixels
///
////////////////////////////////////////////////////////////
void setMouseCursor(const Cursor& cursor);

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Did we discuss why we only have a setter and no getter?

Is this a real reference or are we copying the cursor internally? We might want to document whether the cursor object can be discarded or needs to live on.

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

Did we discuss why we only have a setter and no getter?

I don't think we did. But in any case, we don't do it for the other properties (e.g. visibility, ...).

We might want to document whether the cursor object can be discarded or needs to live on.

The doc reads:

The cursor must not be destroyed while in use

and

The behaviour is undefined if the cursor is destroyed while in use by the window.

;-)

/// hotspot is the pixel coordinate within the cursor image
/// which will be located exactly where the mouse pointer
/// position is. Any mouse actions that are performed will
/// return the window/screen location of the hotspot.

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

I assume hotspot is a technical and commonly used term, correct?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

Correct: it's used by Linux, Windows and Apple.

////////////////////////////////////////////////////////////
bool Cursor::loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hotspot)
{
if ((pixels == 0) || (size.x == 0) || (size.y == 0))

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

What's with the case where the hotspot isn't within the given size?
If it can be outside the given size, why can't there be negative values (Vector2u)?
If it can't be outside the given size, we probably want to either fail (don't hide error, but unclear failure) or limit to the max size (hiding error).

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

Some OS might support a hotspot outside the image, but it wouldn't make sense in practice. Here I think I followed what @binary1248 did in his initial version, but for V3 we might reconsider this and throw something instead when the values are not the one expected.

{
switch (type)
{
default: return false;

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Shouldn't the default be at the end of the case list? (Isn't it even legal to always use default if the cases are written after the default? Or maybe that's just some old C stuff, I've once read somewhere.)

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

It's perfectly valid C++98 code. I think we do elsewhere as well, but in any case I think it improves readability. What do you think?

/// \warning On Unix, the pixels are mapped into a monochrome
/// bitmap: pixels with an alpha channel to 0 are
/// transparent, black if the RGB channel are close
/// to zero, and white otherwise.

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

So Linux only supports monochrome cursors?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

As far as I could find in the X11 documentation, yes.

This comment has been minimized.

Copy link
@MarioLiebisch

MarioLiebisch Apr 5, 2017

Member

On Windows it's hardware dependent, although I don't think we've still got any current and supported GPUs that don't support colored cursors.

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 5, 2017

Member

I took @binary1248 impl for Win32 so I didn't look at the documentation that much, but it appears you're right:

Cursors can be either monochrome or color, and either static or animated. The type of cursor used on a particular computer system depends on the system's display. Old displays such as VGA do not support color or animated cursors. New displays, whose display drivers use the device-independent bitmap (DIB) engine, do support them.

Question is, should we document this or do we assume such old systems are not used with SFML?

This comment has been minimized.

Copy link
@MarioLiebisch

MarioLiebisch Apr 5, 2017

Member

I'd say it's safe to ignore, considering we want to use modern OpenGL anyway? Even my old 1 MB Cirrus Logic from 1996 supported colored/animated hardware cursors, although I remember some later games claiming otherwise. :)

XFreePixmap(m_display, maskPixmap);

// We assume everything went fine...
return true;

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

So there's really no way this function could ever fail?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

X11 being async, I think we simply cannot check for failure. Maybe some X11-guru can confirm that, but I've found nothing in the doc while writing this code to catch errors, sadly.

unsigned int shape;
switch (type)
{
default: return false;

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Again, should come after the cases.

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

same. :-)

@@ -265,7 +274,8 @@ class WindowImplX11 : public WindowImpl
XIC m_inputContext; ///< Input context used to get unicode input in our window
bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid

This comment has been minimized.

Copy link
@eXpl0it3r

eXpl0it3r Apr 4, 2017

Member

Why exactly is it ::Cursor here?

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 4, 2017

Member

To avoid a clash with sf::Cursor and X11 cursor, which lives in the global namespace.

This comment has been minimized.

Copy link
@MarioLiebisch

MarioLiebisch Apr 5, 2017

Member

While at those lines, I'm pretty sure it's "hiding" vs. "hidding". ;)

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 5, 2017

Member

huhu, yes!

@mantognini
Copy link
Member

left a comment

Remove #include <SFML/Config.hpp> from ContextSettings.hpp.

this change is actually needed.

@mantognini
Copy link
Member

left a comment

Typo to fix

@@ -265,7 +274,8 @@ class WindowImplX11 : public WindowImpl
XIC m_inputContext; ///< Input context used to get unicode input in our window
bool m_isExternal; ///< Tell whether the window has been created externally or by SFML
int m_oldVideoMode; ///< Video mode in use before we switch to fullscreen
Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one
::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hidding, we must create a transparent one

This comment has been minimized.

Copy link
@mantognini

mantognini Apr 5, 2017

Member

s/hidding/hiding

@eXpl0it3r eXpl0it3r moved this from Requires Adjustments to Review & Testing in SFML 2.5.0 Apr 25, 2017

@mantognini mantognini force-pushed the feature/set_cursor branch from 9a9acf7 to 6ba2bb1 Apr 26, 2017

@mantognini

This comment has been minimized.

Copy link
Member

commented Apr 26, 2017

I've fixed the type and rebased. Let me know if there's anything else!

@eXpl0it3r

This comment has been minimized.

Copy link
Member

commented Jun 3, 2017

I tested this change on Windows 10 with the small testing code from this gist.

Everything seems to work fine. All the system cursors are showing and my custom cursor are working. During testing I came up with two questions:

  • When you create a window, the cursor is set to the system arrow cursor, but once you start using an sf::Cursor is there a way to reset the default cursor? Or do you now have to keep sf::Cursor alive throughout the whole application?
  • The wait cursor (and maybe others) are usually animated cursors (spinning circle, hour glass, etc.), but with SFML the cursors are just static. Has this been done just for consistency among platforms? Or were there other reasons? What would it take to make them animated?
@mantognini

This comment has been minimized.

Copy link
Member

commented Jun 5, 2017

Thanks for giving it a shot! :-)

When you create a window, the cursor is set to the system arrow cursor, but once you start using an sf::Cursor is there a way to reset the default cursor? Or do you now have to keep sf::Cursor alive throughout the whole application?

While defined as "undefined behaviour", if you reset the cursor to the default arrow one, I think all implementation will survive the destruction of the Cursor instance. This is not perfect, but could easily be improved with the help of shared_ptr -- I just don't think the investment is worth it for SFML 2.x branch to implement something similar.

The wait cursor (and maybe others) are usually animated cursors (spinning circle, hour glass, etc.), but with SFML the cursors are just static. Has this been done just for consistency among platforms? Or were there other reasons? What would it take to make them animated?

This wasn't implemented intentionally, but supporting it is tricky. On Linux, it would be a pain in the neck to do it from what I recall (well, like anything I should say...). With Win32, it should be easier to do it, I guess, but I don't know how to get the right resource for the animated cursor. And I think this is a feature not (publicly) exposed on Mac.

@eXpl0it3r

This comment has been minimized.

Copy link
Member

commented Jun 5, 2017

Thanks for clarifying.

I think both points are valid discussions we should come back to at one point, but as a first step, I think this is ready to be merged.

@eXpl0it3r eXpl0it3r moved this from Review & Testing to Ready in SFML 2.5.0 Jun 5, 2017

@mantognini

This comment has been minimized.

Copy link
Member

commented Jun 5, 2017

I think both points are valid discussions we should come back to at one point

Yes, definitely.

@mantognini mantognini added s:accepted and removed s:undecided labels Jul 1, 2017

@eXpl0it3r eXpl0it3r force-pushed the feature/set_cursor branch from 6ba2bb1 to 34ea68b Jul 10, 2017

@eXpl0it3r eXpl0it3r merged commit 34ea68b into master Jul 18, 2017

15 checks passed

android-armeabi-v7a-api13 Build #179 done.
Details
debian-gcc-64 Build #450 done.
Details
freebsd-gcc-64 Build #412 done.
Details
osx-clang-el-capitan Build #299 done.
Details
static-analysis Build #420 done.
Details
windows-gcc-492-tdm-32 Build #316 done.
Details
windows-gcc-492-tdm-64 Build #312 done.
Details
windows-gcc-630-mingw-32 Build #65 done.
Details
windows-gcc-630-mingw-64 Build #66 done.
Details
windows-vc11-32 Build #423 done.
Details
windows-vc11-64 Build #421 done.
Details
windows-vc12-32 Build #420 done.
Details
windows-vc12-64 Build #421 done.
Details
windows-vc14-32 Build #420 done.
Details
windows-vc14-64 Build #420 done.
Details

@eXpl0it3r eXpl0it3r moved this from Ready to Merged in SFML 2.5.0 Jul 18, 2017

@eXpl0it3r eXpl0it3r deleted the feature/set_cursor branch Jul 18, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can鈥檛 perform that action at this time.