Mir

Merge lp:~mir-team/mir/cursor-spike-phase-1-resubmit into lp:mir

Proposed by Robert Carr
Status: Superseded
Proposed branch: lp:~mir-team/mir/cursor-spike-phase-1-resubmit
Merge into: lp:mir
Diff against target: 950 lines (+363/-116)
24 files modified
examples/render_surfaces.cpp (+15/-64)
include/platform/mir/graphics/cursor.h (+4/-1)
include/platform/mir/graphics/cursor_image.h (+45/-0)
include/platform/mir/graphics/display.h (+3/-2)
include/server/mir/default_server_configuration.h (+10/-0)
include/server/mir/graphics/cursor_images.h (+54/-0)
include/test/mir_test_doubles/mock_display.h (+1/-1)
include/test/mir_test_doubles/null_display.h (+4/-1)
src/platform/graphics/android/android_display.cpp (+2/-2)
src/platform/graphics/android/android_display.h (+1/-1)
src/platform/graphics/mesa/cursor.cpp (+12/-8)
src/platform/graphics/mesa/cursor.h (+5/-2)
src/platform/graphics/mesa/display.cpp (+13/-8)
src/platform/graphics/mesa/display.h (+3/-2)
src/server/default_server_configuration.cpp (+4/-7)
src/server/graphics/CMakeLists.txt (+1/-0)
src/server/graphics/builtin_cursor_images.cpp (+53/-0)
src/server/graphics/builtin_cursor_images.h (+52/-0)
src/server/graphics/default_configuration.cpp (+35/-0)
src/server/graphics/nested/nested_display.cpp (+2/-2)
src/server/graphics/nested/nested_display.h (+1/-1)
src/server/graphics/offscreen/display.cpp (+1/-1)
src/server/graphics/offscreen/display.h (+1/-1)
tests/unit-tests/graphics/mesa/test_cursor.cpp (+41/-12)
To merge this branch: bzr merge lp:~mir-team/mir/cursor-spike-phase-1-resubmit
Reviewer Review Type Date Requested Status
Alan Griffiths Needs Fixing
Daniel van Vugt Needs Fixing
Chris Halse Rogers Approve
Alberto Aguirre (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Kevin DuBois Pending
Review via email: mp+211598@code.launchpad.net

This proposal supersedes a proposal from 2014-03-17.

This proposal has been superseded by a proposal from 2014-04-23.

Commit message

Extract the Cursor image from the builtin mesa cursor and normalize Cursor construction.

Description of the change

This is phase 1 of a spike I am doing on the cursor code.

My goal is to enable a client cursor API, that is enable clients to request cursors and enable/disable the cursor.

This branch just contains some first steps:

1. Normalize the treatment of the Cursor object, that is to say deal with it like other objects on the server configuration. Display::the_cursor is moved to DefaultServerConfiguration, and Display::create_hardware_cursor becomes a factory function. With Display::the_cursor it was hard to inject dependencies in to the cursor (where do you pass the CursorRepository? to the display constructor?...seems weird).
2. Introduce CursorImage and CursorRepository. Port the builtin cursor image to this interface and port the mesa hardware cursor to use CursorImage.

Next steps planned:
1. Build XCursor theme based CursorRepository for loading themed cursors.
2. Enable client API for setting cursor (using a similar style to display mediator, focused client can set the cursor, including disabling it)

Thanks!

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Please resubmit and move black_arrow.c using "bzr mv" so it's not bloating the diff.

review: Needs Resubmitting
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

black_arrow.c isn't an important file to track the history of, I admit. But in general we should "bzr mv" to ensure files don't lose history when they move. And doing a move would omit its contents from the diff too, halving the size of the merge proposal.

Revision history for this message
Kevin DuBois (kdub) wrote : Posted in a previous version of this proposal

> black_arrow.c isn't an important file to track the history of, I admit. But in
> general we should "bzr mv" to ensure files don't lose history when they move.
> And doing a move would omit its contents from the diff too, halving the size
> of the merge proposal.

+1

Revision history for this message
Kevin DuBois (kdub) wrote : Posted in a previous version of this proposal

16 +#include <assert.h>
17 +
1551 +#include <assert.h>
1552 +
needed?

CursorImage might better be a mir::scene::PixelBuffer

1568 + const int cursor_width = 64;
1569 + const int cursor_height = 64;
const ordering and magic numbers, maybe black_arrow.width/height

1665 + static const geometry::Size default_cursor_size = {geometry::Width{64},
1666 + geometry::Height{64}};
const ordering and why static

mg::BuiltinCursorRepository::lookup_cursor()
I'd expect a repository to be something more like a std::map, this seems to allocate a new object during every lookup.

1652 +mir::DefaultServerConfiguration::the_cursor()
can we assume we only have one cursor? The nexus 4 could potentially have 4 cursors that are hardware accelerated.

+ return the_display()->create_hardware_cursor(the_default_cursor_image());
This is a pre-existing issue, but it strikes me that its very strange that the display interface allocates the hardware cursor, when the display interface does not even allocate the framebuffers. A hardware cursor is a special buffer, it should be allocated from mg::GraphicBufferAllocator

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Also remember to move black_arrow.xcf to wherever black_arrow.c goes.

Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

Fixed assert and constant stuff.

>> mg::BuiltinCursorRepository::lookup_cursor()
>> I'd expect a repository to be something more like a std::map, this seems to allocate a new object during
>> every lookup.

Probably for the themed cursors, it didnt seem worth defining the ctor in this case since its only called once, but I decided there is no harm and changed it to define a ctor so we can just return the same instance.

Strictly this code is probably less performant ;) as there is no runtime difference (only called once) and the non defaulted ctor increases binary size. Still I dont think there is any harm anyway.

>> CursorImage might better be a mir::scene::PixelBuffer

Ive changed raw_argb to use as_argb_8888 as in the style of PixelBuffer.

For now the PixelBuffer interface is obviously a bit specific:
/*
 * Interface for extracting the pixels from a graphics::Buffer.
 */

Conceptually they both implement some sort of mir::graphics::Pixels/mir::graphics::Image interface, I dont think we need to spend time on it until there is a consumer though.

>> can we assume we only have one cursor? The nexus 4 could potentially have 4 cursors that are hardware
>> accelerated.

No we cant assume this forever. For now though I have a pretty simple target, mostly for the desktop preview/xmir:

A single themeable cursor controllable by client API (controllable, as in: theme, enable/disable).

I dont think we have any requirements for any multi cursor stuff now.

>> + return the_display()->create_hardware_cursor(the_default_cursor_image());
>> This is a pre-existing issue, but it strikes me that its very strange that the display interface
>> allocates the hardware cursor, when the display interface does not even allocate the framebuffers. A
>> hardware cursor is a special buffer, it should be allocated from mg::GraphicBufferAllocator

I agree its weird. Ill fix up this in one of the next branches as I clean up the GBM cursor a little more. This branch is mostly about encapsulating the cursor image and cursor in the server configuration so that work on cursor implementation independent code can continue (cursor theme loader, client API for cursor image control) in parallel to the cursor backend work.

Thanks!

Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

Tried to revert black_arrow.c and then move it but bzr doesnt like that...ust a sec

Revision history for this message
Robert Carr (robertcarr) wrote : Posted in a previous version of this proposal

Fixed black_arrow.c...was there a better way to do that than recreate the entire branch?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

No diff, no code?!

review: Needs Resubmitting
Revision history for this message
Chris Halse Rogers (raof) wrote : Posted in a previous version of this proposal

Apart from the lack of code… assuming the diff is roughly equivalent to the previous submission:

I'd prefer create_hardware_cursor to return a unique_ptr; it's easy to go unique→shared, but not possible the other way. It also makes it clear that the factory doesn't have a reference. (This applies to basically all our factory methods, to be fair)

----
2. Enable client API for setting cursor (using a similar style to display mediator, focused client can set the cursor, including disabling it)

This is the wrong way approach, I think. The focused client should not be able to set the cursor; a client should be able to set how the cursor appears over their surface.

Yes, that requires work down the input mines...

While I'm on the subject of cursor handling, I think in the post-HWC-enablement world we should be leaving it to the compositor. I'll catch you on IRC perhaps.

Revision history for this message
Robert Carr (robertcarr) wrote :

Not sure what the missing diff was about...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

#1

> Probably for the themed cursors, it didnt seem worth defining the ctor in this case since its only called
> once, but I decided there is no harm and changed it to define a ctor so we can just return the same instance.
>
> Strictly this code is probably less performant ;) as there is no runtime difference (only called once) and the
> non defaulted ctor increases binary size. Still I dont think there is any harm anyway.

I'm not so concerned about the performance either here. It seems in the superseded review the make_shared() was in lookup_cursor, and in this one its in the constructor of BuiltinCursorRepository, so this is okay by me.

Revision history for this message
Kevin DuBois (kdub) wrote :

#2
I guess my main point I have to make is that a hardware cursor, should be modelled just like a buffer in the parts of the system that don't care about hardware bits. (much like mg::Buffer could have any sort of buffer backing it, with different flags and use cases)

On mesa, mg::BufferAllocator::allocate_buffer(mg::BufferUsage::hardware_cursor) could give the little specialised region reserved for the hardware buffer. On android, it could return any old a hardware-capable buffer. At the time of posting, if the mesa stuff finds the flags on the topmost layer is the 'hardware cursor' buffer, then it can use the hardware cursor.

With the ms::PixelBuffer point, it seems funny to have:
class CursorImage
{
  virtual void const* as_argb_8888() = 0;
  virtual geometry::Size size() = 0;
};

and

class PixelBuffer
{
  virtual void fill_from(graphics::Buffer& buffer) = 0;
  virtual void const* as_argb_8888() = 0;
  virtual geometry::Size size() const = 0;
  virtual geometry::Stride stride() const = 0;
}

which both model software access to a buffer.

Maybe this means:
mg::SoftwareBuffer
{
    virtual void const* as_argb_8888() = 0;
    virtual geometry::Size size() const = 0;
    virtual geometry::Stride stride() const = 0;
};
ms::PixelBuffer : public mg::SoftwareBuffer
{
    virtual void fill_from(graphics::Buffer& buffer) = 0;
}

Revision history for this message
Chris Halse Rogers (raof) wrote :

+1 on Kevin's point #2, with the additional point that there isn't any special region reserved for the hardware cursor; AFAIK the only restriction is that the buffer must be 64x64 (on most cards, 128x128 is an option on newer radeons, for example).

The cursor image really is just a buffer.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :
Download full text (5.0 KiB)

I am not sure sure about the cursor image/pixel buffer stuff, the cursor image is something that you will upload to a buffer (a buffer which while not being a special buffer in the buffer sense is given special status in the server), pixel buffer instead models something which you read OUT of a buffer. Of course it seems there is some sort of mg::Pixels base.

RAOF and I had a discussion on the Cursor is just a buffer thread:
22:42 < RAOF> racarr_: You're thinking cursors?
22:43 -!- FunnyLookinHat [~funnylook@ubuntu/member/funnylookinhat] has quit [Quit: Leaving]
22:44 < racarr_> RAOF: Yes. and wondering wwhat you were thinking
22:45 < RAOF> Cool.
22:45 < racarr_> as to the small bits that were on launchpad...uh
22:45 < racarr_> it was a mild hallucination that doing it per connection was going to be easier and also a useful step towards per surface so I ust imagined doing that first
22:45 < RAOF> Hah!
22:45 < racarr_> but have now decided it was silly and am on to mir_surface_set_cursor
22:45 < racarr_> and um...
22:45 < RAOF> It probably _would_ be easier to do per-connection :)
22:46 < racarr_> well but you have to write code that ends up not being useful
22:46 < racarr_> so its pretty pointless
22:46 < RAOF> Yeah.
22:46 < RAOF> It's not a step towards per-surface cursors, which is what you need.
22:46 < racarr_> Also re:create_hardware_cursor
22:46 < racarr_> and unique/shared here it actually is shared
22:46 < racarr_> with the GBM::Display which manipulates it in case of pause/stop, etc.
22:47 < racarr_> which is mostly why it hasnt been moved to some more appropriate place of allocation yet
22:47 < RAOF> Urgh.
22:47 < racarr_> err pause/resume
22:47 < racarr_> to restore the position iirc
22:48 < RAOF> Yeah, because it's all wrong! :)
22:48 < racarr_> lol
22:48 < racarr_> what are your thoughts :D
22:49 < RAOF> So, what I think should be the final state is: The cursor is a renderable in the scene, and the compositor is responsible for getting it on the screen.
22:50 < RAOF> Under GBM the compositor will check if the topmost renderable in the scene is (a) the existing hardware cursor or (b) usable as a HW cursor, and do the API
              twiddling necessary.
22:50 < racarr_> ok
22:51 < RAOF> Under Android the HWC machinery will translate into basically a HW cursor anyway
22:51 < racarr_> Yes
22:51 < RAOF> So I think all the Cursor API wants to be is basically a pointer at a specific renderable.
22:52 < racarr_> hmm well I mean
22:52 < RAOF> If we want a server cursor API at all, I guess?
22:52 < racarr_> it seems like it still needs to be promoted to the server configuration and
22:52 < racarr_> from the perspective of mg::Cursor
22:52 < racarr_> RAOF: Well we are baking window management in to mir
22:53 < racarr_> so the surface cursor selection has to work by default
22:53 < racarr_> and input should be hooked up to the cursor, etc
22:53 < RAOF> Oh, yeah.
22:53 < RAOF> That was just rambling as to what extent we need a Mir-private Cursor API.
22:53 < RAOF> Or whether the various bits could just prod the relevant Renderable.
22:55 < RAOF> It's probably cleaner to have an actual interface to prod, though.
22:55 < racarr_> hmm
22:55...

Read more...

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(3) render_surfaces now segfaults on the first movement of the cursor:
(gdb) bt
#0 0x00007f0a4d0703de in mir::DefaultServerConfiguration::DefaultCursorListener::cursor_moved_to (this=0x96f9f8, abs_x=6, abs_y=0)
    at /home/dan/bzr/mir/tmp.cursor/src/server/default_server_configuration.cpp:101
#1 0x00007f0a4d117292 in mir::input::android::PointerController::notify_listener (this=0x11a3150)
    at /home/dan/bzr/mir/tmp.cursor/src/server/input/android/android_pointer_controller.cpp:56
#2 0x00007f0a4d117597 in mir::input::android::PointerController::setPosition (
    this=0x11a3150, new_x=6, new_y=-5)
    at /home/dan/bzr/mir/tmp.cursor/src/server/input/android/android_pointer_controller.cpp:112

(4) I agree mir::graphics::Buffer half fits what we need for CursorImage. Perhaps we need to keep the commonality in Buffer and move the surface-only stuff into a "SurfaceBuffer" or "ClientBuffer". Then CursorImage becomes a simple implementation of Buffer. Although such a change could come any time later...

(5) CursorRepository is a bad abstraction and the confusing word "Repository" is the reason. A repository of cursors is not a familiar concept even to a graphics programmer. I think the cursor theme should be the main object there. So instead of:
    img = repository->lookup_cursor(theme_name, cursor_name, size);
a better design would be:
    img = theme->get_image(cursor_name, size);

Remember to link the relevant bugs when ready:
https://bugs.launchpad.net/mir/+bugs?field.tag=cursor

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

+1 for RAOF's thoughts at 22:49-22:51 and for Daniel's points.
As for the cursor always being on top of everything, it seems a bit more flexible not to bake that into mir and have that be a decision of the shell stuff. (I could see someone wanting a cursor to go under a pop-up notification maybe)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Another issue:

(6) I think it would scale/port better to more toolkits for us to choose the cursor type using an enum. Otherwise we will find ourselves having to translate toolkit requirements into strings on the client side, which is less efficient and not strongly typed like an enum is.
    251 + std::string const& cursor_name,
Something like this would be good:
    img = cursor_theme->get_image(mir_cursor_crosshair, size);

Revision history for this message
Robert Carr (robertcarr) wrote :

3. Fixed. Whoops! Lost track during refactoring and was returning a nullptr rather than a null cursor instance.
4. Im still not sure what we mean here. mir::graphics::Buffer is a GPU side buffer (which could be mapped). mir::graphics::CursorImage is just a way to access (CPU side, for example for upload to a buffer), the pixels of a loaded cursor image (loaded from the on disk theme). Maybe you mean mir::graphics::PixelBuffer. In this case I agree there is a common mg::Image/mg::Pixels interface they implement (raw_argb, size). Of course we shouldnt get overexcited about making classes implement interfaces until we have someone who wishes to consume them through that interface.
5. What loads the Cursor theme then?
6. I chose string/string on the suggestion of RAOF...and that afaik it is what toolkits will be best prepared to deal with (working like other cursor APIs).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(3) render_surfaces: Confirmed it no longer crashes. I think this is a bad example though. Setting a NullCursor is not how anyone will hide the cursor in reality. That should be a separate show/hide function. Maybe as an example you could just change NullCursor to some kind of SimpleCursor (but visible).

(4) Yes, Buffer is presently a GPU-side buffer. My suggestion describes how it might be generalized to represent any type of buffer though. Not yet useful, but plausibly in future. Not a blocker.

(5) Good point. But "Repository" still feels very counter-intuitive. Perhaps it's the graphics platform which determines and constructs available themes. Or maybe it's the Display. Or to just be generic, let it be a CursorThemeFactory which produces CursorTheme's, from which you can load a known cursor image.

(6) Converting between strings and enums is not a big deal. However I think there is sufficient evidence that enums are a better fit for plenty of toolkits:
   https://developer.gnome.org/gdk3/stable/gdk3-Cursors.html#GdkCursorType
   http://qt-project.org/doc/qt-5/qt.html#CursorShape-enum
   http://www.opengl.org/resources/libraries/glut/spec3/node28.html#SECTION000513000000000000000
And I think enums are a better fit for apps too, which simply want to say "give me an arrow/crosshair/bottom-right-corner".

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

P.S. Cursor themes don't have to be complicated or even depend on X libraries. We could for example say that a cursor theme is a single image (dimensions WxH) loaded any way you like. And cursor type N can be found at square x=N*H,y=0,w=H,h=H. In that case all you need to produce a cursor theme is an image editor. And no dependencies on legacy X code.

Revision history for this message
Robert Carr (robertcarr) wrote :

>> That should be a separate show/hide function. Maybe as an example you could just change NullCursor to some >> kind of SimpleCursor (but visible).

What do you mean by SimpleCursor. Not sure what render_surfaces cursor stuff is intended to be an example of really. A later branch in this spike introduces cursor show/hide as part of the general control API. Maybe change it then?

>> (5) Good point. But "Repository" still feels very counter-intuitive. Perhaps it's the graphics platform
>> which determines and constructs available themes. Or maybe it's the Display. Or to just be generic, let it >> be a CursorThemeFactory which produces CursorTheme's, from which you can load a known cursor image.

Its certainly not the display or graphics platform. In an upcoming branch there is a component which changes the cursor image based on entering/leaving surfaces (and what these surfaces have requested for their cursor). Certainly this component should not depend on the entire Display/Graphics platform interface. Plus the cursor theme loader doesnt vary with graphics platform.

In this case Repository is a little more honest than Factory because ownership of the images is shared with the creator which acts as a cache (or generically: repository) of the loaded images.

>> (6) Converting between strings and enums is not a big deal. However I think there is sufficient evidence >> that enums are a better fit for plenty of toolkits:

Maybe RAOF will have some insight?

Revision history for this message
Chris Halse Rogers (raof) wrote :

1) I don't think there's any value in not using existing XCursor themes. There's nothing wrong with them, and it's one less point of friction.

2) I think strings for the cursor-lookup API are going to be a better idea than enums. While toolkits generally provide a set of enums for common cursors, they *also* provide the ability to look up arbitrary cursors. This also lets clients load their own themes with semantically useful identifiers.

Overall, I think this is a step in the right direction, and will approve as such.

Review nits:
185 + virtual std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image) = 0;
Should take a std::shared_ptr<CursorImage const> const& (or a std::unique_ptr, of course ☺), to make it obvious that create_hardware_cursor isn't going to mutate the image.

Relatedly, all the public members of CursorImage and CursorRepository want to be const.

I'd really like it if create_hardware_cursor returned a std::unique_ptr; I think we would gain clarity of access and mutation - and thread-obviousness - if we weren't sharing all our state, all the time :)

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(3) NullCursor/SimpleCursor: As NullCursor is not a realistic way to hide the cursor (eventually), we should instead demo the functionality being introduced. That is put a few pixels in there and make it a simple visible cursor.

(5) "Repository" is still a very ambiguous word. As such, people (including myself) can't figure out what it is or where such a thing would or should be instantiated. If your class design necessitates ambiguous words then that's a sign you need a different class design.

(6) XCursor themes are a stopgap. They shouldn't drive future architecture. The above links all show toolkits and apps fit better with enums. And we can still translate them so XCursors can be used in the mean time.

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

I think we disagree about (6) - the toolkits all _support_ the use of enums for common cursors, but they also support the use of strings (and, indeed, arbitrary images). I'm perfectly happy for the client API for this to have two entry points - mir_surface_set_standard_cursor(MirCursorType im_an_enum) and mir_surface_set_cursor_from_theme(MirCursorTheme theme, char const* name).

Going further, I'd like clients to be able to load cursor themes and index into them; in this case, string indices are mandatory (or, at least, really useful).

Revision history for this message
Robert Carr (robertcarr) wrote :

>>Review nits:
>>185 + virtual std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image) = 0;
>>Should take a std::shared_ptr<CursorImage const> const& (or a std::unique_ptr, of course ☺), to make it obvious that >>create_hardware_cursor isn't going to mutate the image.

>> Relatedly, all the public members of CursorImage and CursorRepository want to be const.

Fixed

>>1) I don't think there's any value in not using existing XCursor themes. There's nothing wrong with them, and it's one less >>point of friction.

I agree.

----------------------

>> (3) NullCursor/SimpleCursor: As NullCursor is not a realistic way to hide the cursor (eventually), we should instead demo >> the functionality being introduced. That is put a few pixels in there and make it a simple visible cursor.

There is also a simple visible cursor, however the example previously came with an option to totally disable the cursor so I preserve that.

>> (5) "Repository" is still a very ambiguous word. As such, people (including myself) can't figure out what it is or where >> >> such a thing would or should be instantiated. If your class design necessitates ambiguous words then that's a sign you need >> a different class design.

Are you being serious about the word repository or just trying to prove a point re: your opinion on object architecture? I am only aware of two main definitions of repository: 1. A location where things are stored and offered. 2. A burial place. Mulled it over the weekend and do not see the confusion.

Still I am not particular attached to the word. How about CursorLoader as in the most recent revision. This fails to capture the aspect that you share ownership with the loader, but that information is also in the shared_ptr signature so I do not think it is a big loss.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(3) ExampleCursorImage doesn't seem to do anything(?). I can just get none (default) or the standard black arrow with --display-cursor=1. Looks like the bug pre-dates this proposal, but it makes the proposed changes untestable.

(5) Yes I am serious about "Repository". Names are important because a well chosen one leads to universal understanding, high productivity and avoids refactoring. Poorly chosen names lead to confusion and thus slow development or even bugs. The word "Repository" I know means where you put or get something. So is the cursor coming or going from a "repository"? It's not clear from the name. A realistic mental model for cursors is that you put it in on the screen, but you get it from a theme. Those are more concrete nouns that will lead to wider understanding and better code reuse/utilization. "CursorLoader" is mildly better, but it makes the mistake of using a verb in the noun, which tends to limit how well multiple people can agree on what it represents. If there is a more concrete noun available like Theme or Factory then I'd prefer we used it.

(6) Per these links:
   https://developer.gnome.org/gdk3/stable/gdk3-Cursors.html#GdkCursorType
   http://qt-project.org/doc/qt-5/qt.html#CursorShape-enum
   http://www.opengl.org/resources/libraries/glut/spec3/node28.html#SECTION000513000000000000000
you can see there's going to be a fair amount of mapping between enums and/or strings. While strings are completely flexible, it's more efficient and simpler to map between enum values. That said, I'm not sure that's a performance/convenience penalty worth blocking on, so won't.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :

CursorLoader->CursorImages

The code in render surfaces was only triggered when input was disabled (to provide...some sort of cursor animation when running examples as non root?!), it seems bad to start mir with input disabled (you cant quit), and this didnt seem to be a particularly important example so Ive removed it.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(3) NullCursor is not a realistic way to hide the cursor. We should either display a simple custom cursor or not modify render_surfaces at all in this proposal. NullCursor is not a useful example or manual test at all. Because showing nothing means you can't really tell it's working properly. And in reality when you do want "no cursor" you will probably toggle a flag, but not set a NullCursor.

(7) "CursorImages" vs "theme": In my mind cursor images come from a theme, but you're selecting a theme from CursorImages. That sounds backwards and will be hard to understand/extend/use:

304 +class CursorImages
305 +{
306 +public:
307 + /// Looks up the image for a named cursor in a given cursor theme. Cursor names
308 + /// follow the XCursor naming conventions.
309 + virtual std::shared_ptr<CursorImage> lookup_cursor(std::string const& theme_name,
310 + std::string const& cursor_name,
311 + geometry::Size const& size) = 0;
312 +

The most realistic (IMHO) interpretation of "cursor images" is a set of cursor images provided by a theme. In that way the cursor images concept is a subset of the theme, not the superset. Maybe there does exist a class name where this design makes sense, but not "CursorImages".

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

This suggests:

class CursorTheme
{
public:

virtual std::unique_ptr<CursorImage> lookup_cursor(std::string const& name, geometry::Size const& size) const = 0;
}

Plus

class XCursorTheme : public CursorTheme
{
public:
XCursorTheme(std::string const& name);
}
?

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Chris: Yeah I suggested CursorTheme above on 2014-03-21. And Robert rightfully pointed out you still need a factory to abstract the construction of a CursorTheme. But I think that's probably OK. And it has the added benefit of not implying that all your themes are loaded and available simultaneously, because they probably shouldn't be.

On the other hand, I'm not 100% against them being a single class as it is designed right now. But the proposed names of "CursorRepository" and "CursorImages" don't describe the design clearly and unambiguously enough.

Revision history for this message
Chris Halse Rogers (raof) wrote :

CursorImageLoader?

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

875 +void const* StubCursorImage::image_data = reinterpret_cast<void*>(0x1729);

It is non-portable to reinterpret 0x1729 as a pointer? Can't we use a sensible value. E.g.

void const* StubCursorImage::image_data = &image_data;

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

reading through the comments there seem to be three issues in play:

1. How to model not having a cursor.
2. The interface name for sourcing images ("CursorImages" in the current revision)
3. Whether cursor themes are textual labels or objects in their own right.

1. How to model not having a cursor.
===================================

I like NullObject pattern for a lot of things but in this case something is wrong about it.

Looking at the mg::Cursor interface it isn't a cursor, it is a sink for cursor updates (motion and image changes). Having a Null version of an event sink makes more sense than a null cursor.

Maybe mg::Cursor has the wrong name and should be mg::CursorModifier. NullCursorModifier doesn't seem strange.

2. The interface name for sourcing images
=========================================

I'm not entirely convinced by "Loader" as that loading is an implementation detail. In fact, the "Loader" implementations in the MP don't load anything.

The current name "CursorImages" doesn't seem too bad in itself, but this doesn't read well:

 "the_cursor_images()->lookup_cursor("default", "arrow", default_cursor_size);"

How about something like:

  "the_cursor_store()->set_cursor(the_cursor_modifier(), "default", "arrow", default_cursor_size);"

3. Whether cursor themes are textual labels or objects
======================================================

I don't know enough about cursors to have an opinion.

Revision history for this message
Robert Carr (robertcarr) wrote :

But 1729 was a perfectly reasonable choice, its the smallest number expressible as the sum of 2 cubes in 2 ways...ok but it's gone.

Renamed lookup cursor:
the_cursor_images->image("default", "arrow", default_cursor_size) -> CursorImage

im pretty happy with this one.

DO you think cursor store is better? Is it better than the original repository? (Repository sounds slightly better to me than store...not sure why...use of store like that isnt so common in american english I guess).

I am trying not to deal with the mg::Cursor (I think you are on to something with a different name, but modifier doesnt strike me) until I establish the cursor API and can think about it a little more clearly.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

I know you are dying to hear more naming suggestions so here's mine :) - given the current CursorImages interface it leads itself to CursorImageProvider.

which leads to
the_cursor_image_provider->image_from(...)

Repository for me implies having size/add/remove methods, store - yeah not a fan, I suppose it's the US context - why am I buying cursors? :)

Cursor and NullCursor make sense to me - I think the definition of cursor here makes sense - a movable image. A NullCursor then makes sense - an empty image that doesn't move.

CursorModifier would not make sense to me, as then I would have the following questions - what "cursor" is it modifying? Who implements cursor?

I'm not hung up on the names though functionally it looks fine to me.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

+1 that the existing mir::graphics::Cursor makes sense. It's a nice simple interface for implementing a real working cursor. That makes it rather clear we are only dealing with cursor "themes" and/or "images" in this proposal. Although where "hotspot" will live is not year clear.

At the highest level I think the shell will have a current active (perhaps fixed) cursor theme. So that's something you should be able to query to get an image:
    shell->current_cursor_theme()->get_image("arrow");
or
    config->the_cursor_theme()->get_image("crosshairs");
And an app/surface could have its own too:
    img = surface->cursor_theme()->get_image("custom_cursor_thingy");

Who or what changes the theme is an exercise for later. But first we need to define (like in the above examples) where/what the theme exists. If you change the theme, who do you change it for? I think you change it for:
  (a) the whole shell; or
  (b) a specific surface/app.

Also, once you set a theme, how do you refer to it? In the current design that would be a string. I think that's inefficient. It's a set of images with metadata like hotspot coordinates. The theme sounds like an object to me.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Well if themes are meaningful as objects why don't we want code like:

    auto const new_theme = ???.cursor_theme("default");

    cursor->change_theme_to(new_theme);
    ...
    cursor->change_size_to(new_size);
    ...
    cursor->change_image_to("arrow");

or

    cursor->change_to(new_theme, new_size, "arrow");

I.e. I don't get why the client code is messing with getting and setting CursorImages instead of asking the cursor to talk to its current theme.

review: Needs Information
Revision history for this message
Robert Carr (robertcarr) wrote :

I don't think a cursor theme is particularly useful as an object. I dont see cursor theme changes being handled by the shell swapping out cursor theme objects, rather I would say when the system is reconfigured the default cursor theme changes.

As far as I can see the main point for selecting alternative cursor themes, is for example a game may install an alternate cursor theme, and refer to it by name, as a more convenient form of uploading custom cursors.

Revision history for this message
Chris Halse Rogers (raof) wrote :

This looks to me like a step in the right direction.

Random nit:
309 + virtual std::shared_ptr<CursorImage> image(std::string const& theme_name,
→ virtual std::shared_ptr<CursorImage const> image(...)

Re: cursor themes as objects. The bare minimum required is the ability to associate a CursorImage with a surface, and this CursorImage is going to come from the client - either by reference “give me MIR_CURSOR_CROSSHAIR”, “give me "fancy arrow" from "MySuperGameTheme"”, or by value “set my cursor to <blob of ARGB data>”.

Whether or not we want to *have* a global default cursor theme - so that clients which have specified MIR_CURSOR_CROSSHAIR get their cursor image updated automatically should that theme change - is, I think, something for a later MP.

I would be quite happy with Mir not providing any means to create CursorImages *at all*. It's something that all desktop shells are going to need to do, so I don't think it's out of scope for Mir, but equally it's not something that needs to interact with the rest of Mir at all. We could perfectly happily have a purely client-side library that handles the cursor loading/themes/whatever and hands CursorImages to the server.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alan's concern concerned me too when I read it:
  "I.e. I don't get why the client code is messing with getting and setting CursorImages instead of asking the cursor to talk to its current theme."
But I don't see where client code is messing with CursorImages. Unless you mistake the render_surfaces demo for a client? It's not a client... or a server :)

(7) I'm trying to not be the blocker here and responding to all new comments every day. But I still can't get past the repository/CursorImages stuff. Perhaps as raof alludes to, we shouldn't use the word "theme" at all. Mir doesn't (yet) need themes. So maybe just remove the parameter:
   std::string const& theme_name,
and worry about the "theme" question later. Then it matters much less what "CursorImages" is called.

(3) NullCursor still needs fixing IMHO per comments on 2014-04-02.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Alan's concern concerned me too when I read it:
> "I.e. I don't get why the client code is messing with getting and setting
> CursorImages instead of asking the cursor to talk to its current theme."
> But I don't see where client code is messing with CursorImages. Unless you
> mistake the render_surfaces demo for a client? It's not a client... or a
> server :)

The phrase "client code" shouldn't be misinterpreted like this. We're discussing interface design and in that context it means code that is a client of that interface.

Revision history for this message
Chris Halse Rogers (raof) wrote :

So, given that ‘cursor->change_image_to(<blob of ARGB data>)’ needs to be supported, which roughly implies that CursorImage needs to exist, what blockers, if any, are there for this to land?

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The blockers are:

(7) Remove the "theme_name" parameter to eliminate all confusion and debate over theming. If no one mentions the word "theme" for now then there's less to argue about.

(3) NullCursor still needs fixing per comments on 2014-04-02.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

If we're not messing with the abstraction that mg::Cursor represents then NullCursor makes sense. (I'm unconvinced by the naming as discussed above, but that isn't a blocker for this MP.)

I tend to agree with Daniel about "theme_name" - we don't need any representation of themes (yet).

Is there anything cursor specific about CursorImage? Or is it just a holder for any ARGB image?

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

As it stands, CursorImage is just a holder for any ARGB image, in the future it could grow a hotspot, or come to understand animated cursors, etc....

>> (7) Remove the "theme_name" parameter to eliminate all confusion and debate over theming.
>> If no one mentions the word "theme" for now then there's less to argue about.
----
>>I tend to agree with Daniel about "theme_name" - we don't need any representation of themes (yet).

I think the game cursor theme example is a good use case for themes, we can support it easily. If the only reason is so there "is less" to argue about why waste the time to remove it now and then add it later?

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

The use of arbitrary cursor_name strings eliminates the need for any theme right now, even in the game case. Because your game can just:
    load_cursor("my_special_menu_cursor");

I'd rather not take the approach of land it now and fix it later. It's not clear any theme specification will be required at all in the foreseeable future. And it just creates design disagreements over what a theme represents.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/render_surfaces.cpp'
2--- examples/render_surfaces.cpp 2014-04-08 09:06:37 +0000
3+++ examples/render_surfaces.cpp 2014-04-14 21:12:09 +0000
4@@ -27,6 +27,7 @@
5 #include "mir/geometry/rectangles.h"
6 #include "mir/graphics/buffer_initializer.h"
7 #include "mir/graphics/cursor.h"
8+#include "mir/graphics/cursor_image.h"
9 #include "mir/graphics/display.h"
10 #include "mir/graphics/display_buffer.h"
11 #include "mir/graphics/gl_context.h"
12@@ -89,59 +90,18 @@
13 namespace
14 {
15 std::atomic<bool> created{false};
16-bool input_is_on = false;
17-std::weak_ptr<mg::Cursor> cursor;
18-static const uint32_t bg_color = 0x00000000;
19-static const uint32_t fg_color = 0xffdd4814;
20+
21 static const float min_alpha = 0.3f;
22
23-void update_cursor(uint32_t bg_color, uint32_t fg_color)
24-{
25- if (auto cursor = ::cursor.lock())
26- {
27- static const int width = 64;
28- static const int height = 64;
29- std::vector<uint32_t> image(height * width, bg_color);
30- for (int i = 0; i != width-1; ++i)
31- {
32- if (i < 16)
33- {
34- image[0 * height + i] = fg_color;
35- image[1 * height + i] = fg_color;
36- image[i * height + 0] = fg_color;
37- image[i * height + 1] = fg_color;
38- }
39- image[i * height + i] = fg_color;
40- image[(i+1) * height + i] = fg_color;
41- image[i * height + i + 1] = fg_color;
42- }
43- cursor->set_image(image.data(), geom::Size{width, height});
44- }
45-}
46-
47-void animate_cursor()
48-{
49- if (!input_is_on)
50- {
51- if (auto cursor = ::cursor.lock())
52- {
53- static int cursor_pos = 0;
54- if (++cursor_pos == 300)
55- {
56- cursor_pos = 0;
57-
58- static const uint32_t fg_colors[3] = { fg_color, 0xffffffff, 0x3f000000 };
59- static int fg_color = 0;
60-
61- if (++fg_color == 3) fg_color = 0;
62-
63- update_cursor(bg_color, fg_colors[fg_color]);
64- }
65-
66- cursor->move_to(geom::Point{cursor_pos, cursor_pos});
67- }
68- }
69-}
70+struct NullCursor : public mg::Cursor
71+{
72+ void set_image(std::shared_ptr<mg::CursorImage const> const& /* image */)
73+ {
74+ }
75+ void move_to(geom::Point /* position */)
76+ {
77+ }
78+};
79
80 char const* const surfaces_to_render = "surfaces-to-render";
81 char const* const display_cursor = "display-cursor";
82@@ -280,7 +240,7 @@
83 (surfaces_to_render, po::value<int>()->default_value(5),
84 "Number of surfaces to render")
85 (display_cursor, po::value<bool>()->default_value(false),
86- "Display test cursor. (If input is disabled it gets animated.)");
87+ "Display default cursor");
88
89 return result;
90 }())
91@@ -382,7 +342,6 @@
92 bool composite()
93 {
94 while (!created) std::this_thread::yield();
95- animate_cursor();
96 stop_watch.stop();
97 if (stop_watch.elapsed_seconds_since_last_restart() >= 1)
98 {
99@@ -502,20 +461,15 @@
100 created = true;
101 }
102
103- bool input_is_on()
104- {
105- return the_options()->get<bool>(mo::enable_input_opt);
106- }
107-
108- std::weak_ptr<mg::Cursor> the_cursor()
109+ std::shared_ptr<mg::Cursor> the_cursor()
110 {
111 if (the_options()->get<bool>(display_cursor))
112 {
113- return the_display()->the_cursor();
114+ return me::ServerConfiguration::the_cursor();
115 }
116 else
117 {
118- return {};
119+ return std::make_shared<NullCursor>();
120 }
121 }
122
123@@ -533,9 +487,6 @@
124
125 mir::run_mir(conf, [&](mir::DisplayServer&)
126 {
127- cursor = conf.the_cursor();
128-
129- input_is_on = conf.input_is_on();
130 });
131 ///\internal [main_tag]
132
133
134=== modified file 'include/platform/mir/graphics/cursor.h'
135--- include/platform/mir/graphics/cursor.h 2013-08-28 03:41:48 +0000
136+++ include/platform/mir/graphics/cursor.h 2014-04-14 21:12:09 +0000
137@@ -23,14 +23,17 @@
138 #include "mir/geometry/size.h"
139 #include "mir/geometry/point.h"
140
141+#include <memory>
142+
143 namespace mir
144 {
145 namespace graphics
146 {
147+class CursorImage;
148 class Cursor
149 {
150 public:
151- virtual void set_image(void const* raw_argb, geometry::Size size) = 0;
152+ virtual void set_image(std::shared_ptr<CursorImage const> const& cursor_image) = 0;
153 virtual void move_to(geometry::Point position) = 0;
154
155 protected:
156
157=== added file 'include/platform/mir/graphics/cursor_image.h'
158--- include/platform/mir/graphics/cursor_image.h 1970-01-01 00:00:00 +0000
159+++ include/platform/mir/graphics/cursor_image.h 2014-04-14 21:12:09 +0000
160@@ -0,0 +1,45 @@
161+/*
162+ * Copyright © 2014 Canonical Ltd.
163+ *
164+ * This program is free software: you can redistribute it and/or modify it
165+ * under the terms of the GNU Lesser General Public License version 3,
166+ * as published by the Free Software Foundation.
167+ *
168+ * This program is distributed in the hope that it will be useful,
169+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
170+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
171+ * GNU Lesser General Public License for more details.
172+ *
173+ * You should have received a copy of the GNU Lesser General Public License
174+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
175+ *
176+ * Authored by: Robert Carr <robert.carr@canonical.com>
177+ */
178+
179+
180+#ifndef MIR_GRAPHICS_CURSOR_IMAGE_H_
181+#define MIR_GRAPHICS_CURSOR_IMAGE_H_
182+
183+#include "mir/geometry/size.h"
184+
185+namespace mir
186+{
187+namespace graphics
188+{
189+class CursorImage
190+{
191+public:
192+ virtual void const* as_argb_8888() const = 0;
193+ virtual geometry::Size size() const = 0;
194+
195+protected:
196+ CursorImage() = default;
197+ virtual ~CursorImage() = default;
198+ CursorImage(CursorImage const&) = delete;
199+ CursorImage& operator=(CursorImage const&) = delete;
200+};
201+}
202+}
203+
204+
205+#endif /* MIR_GRAPHICS_CURSOR_IMAGE_H_ */
206
207=== modified file 'include/platform/mir/graphics/display.h'
208--- include/platform/mir/graphics/display.h 2014-01-31 07:48:24 +0000
209+++ include/platform/mir/graphics/display.h 2014-04-14 21:12:09 +0000
210@@ -31,6 +31,7 @@
211 class DisplayBuffer;
212 class DisplayConfiguration;
213 class Cursor;
214+class CursorImage;
215 class GLContext;
216 class EventHandlerRegister;
217
218@@ -97,9 +98,9 @@
219 virtual void resume() = 0;
220
221 /**
222- * Gets the hardware cursor object.
223+ * Create a hardware cursor object.
224 */
225- virtual std::weak_ptr<Cursor> the_cursor() = 0;
226+ virtual std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image) = 0;
227
228 /**
229 * Creates a GLContext object that shares resources with the Display's GL context.
230
231=== modified file 'include/server/mir/default_server_configuration.h'
232--- include/server/mir/default_server_configuration.h 2014-04-14 11:31:04 +0000
233+++ include/server/mir/default_server_configuration.h 2014-04-14 21:12:09 +0000
234@@ -91,6 +91,9 @@
235 class BufferInitializer;
236 class DisplayReport;
237 class GraphicBufferAllocator;
238+class Cursor;
239+class CursorImage;
240+class CursorImages;
241 class GLConfig;
242 namespace nested { class HostConnection; }
243 }
244@@ -158,6 +161,10 @@
245 * dependencies of graphics on the rest of the Mir
246 * @{ */
247 virtual std::shared_ptr<graphics::DisplayReport> the_display_report();
248+ virtual std::shared_ptr<graphics::Cursor> the_cursor();
249+ virtual std::shared_ptr<graphics::CursorImage> the_default_cursor_image();
250+ virtual std::shared_ptr<graphics::CursorImages> the_cursor_images();
251+
252 /** @} */
253
254 /** @name compositor configuration - customization
255@@ -268,6 +275,9 @@
256 CachedPtr<graphics::BufferInitializer> buffer_initializer;
257 CachedPtr<graphics::GraphicBufferAllocator> buffer_allocator;
258 CachedPtr<graphics::Display> display;
259+ CachedPtr<graphics::Cursor> cursor;
260+ CachedPtr<graphics::CursorImage> default_cursor_image;
261+ CachedPtr<graphics::CursorImages> cursor_images;
262
263 CachedPtr<frontend::ConnectorReport> connector_report;
264 CachedPtr<frontend::ProtobufIpcFactory> ipc_factory;
265
266=== added file 'include/server/mir/graphics/cursor_images.h'
267--- include/server/mir/graphics/cursor_images.h 1970-01-01 00:00:00 +0000
268+++ include/server/mir/graphics/cursor_images.h 2014-04-14 21:12:09 +0000
269@@ -0,0 +1,54 @@
270+/*
271+ * Copyright © 2014 Canonical Ltd.
272+ *
273+ * This program is free software: you can redistribute it and/or modify it
274+ * under the terms of the GNU General Public License version 3,
275+ * as published by the Free Software Foundation.
276+ *
277+ * This program is distributed in the hope that it will be useful,
278+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
279+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
280+ * GNU General Public License for more details.
281+ *
282+ * You should have received a copy of the GNU General Public License
283+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
284+ *
285+ * Authored by: Robert Carr <robert.carr@canonical.com>
286+ */
287+
288+
289+#ifndef MIR_GRAPHICS_CURSOR_LOADER_H_
290+#define MIR_GRAPHICS_CURSOR_LOADER_H_
291+
292+#include "mir/geometry/size.h"
293+
294+#include <memory>
295+#include <string>
296+
297+namespace mir
298+{
299+namespace graphics
300+{
301+class CursorImage;
302+
303+/// CursorImages is used to lookup cursor images within cursor themes.
304+class CursorImages
305+{
306+public:
307+ /// Looks up the image for a named cursor in a given cursor theme. Cursor names
308+ /// follow the XCursor naming conventions.
309+ virtual std::shared_ptr<CursorImage> image(std::string const& theme_name,
310+ std::string const& cursor_name,
311+ geometry::Size const& size) = 0;
312+
313+protected:
314+ CursorImages() = default;
315+ virtual ~CursorImages() = default;
316+ CursorImages(CursorImages const&) = delete;
317+ CursorImages& operator=(CursorImages const&) = delete;
318+};
319+}
320+}
321+
322+
323+#endif /* MIR_GRAPHICS_CURSOR_LOADER_H_ */
324
325=== modified file 'include/test/mir_test_doubles/mock_display.h'
326--- include/test/mir_test_doubles/mock_display.h 2014-01-31 07:48:24 +0000
327+++ include/test/mir_test_doubles/mock_display.h 2014-04-14 21:12:09 +0000
328@@ -46,7 +46,7 @@
329 graphics::DisplayResumeHandler const&));
330 MOCK_METHOD0(pause, void());
331 MOCK_METHOD0(resume, void());
332- MOCK_METHOD0(the_cursor, std::weak_ptr<graphics::Cursor>());
333+ MOCK_METHOD1(create_hardware_cursor, std::shared_ptr<graphics::Cursor>(std::shared_ptr<graphics::CursorImage> const&));
334 MOCK_METHOD0(create_gl_context, std::unique_ptr<graphics::GLContext>());
335 };
336
337
338=== modified file 'include/test/mir_test_doubles/null_display.h'
339--- include/test/mir_test_doubles/null_display.h 2014-01-31 07:48:24 +0000
340+++ include/test/mir_test_doubles/null_display.h 2014-04-14 21:12:09 +0000
341@@ -58,7 +58,10 @@
342 }
343 void pause() {}
344 void resume() {}
345- std::weak_ptr<graphics::Cursor> the_cursor() { return {}; }
346+ std::shared_ptr<graphics::Cursor> create_hardware_cursor(std::shared_ptr<graphics::CursorImage> const& /* initial_image */)
347+ {
348+ return {};
349+ }
350 std::unique_ptr<graphics::GLContext> create_gl_context()
351 {
352 return std::unique_ptr<NullGLContext>{new NullGLContext()};
353
354=== modified file 'src/platform/graphics/android/android_display.cpp'
355--- src/platform/graphics/android/android_display.cpp 2014-04-09 09:28:17 +0000
356+++ src/platform/graphics/android/android_display.cpp 2014-04-14 21:12:09 +0000
357@@ -100,9 +100,9 @@
358 {
359 }
360
361-auto mga::AndroidDisplay::the_cursor() -> std::weak_ptr<Cursor>
362+auto mga::AndroidDisplay::create_hardware_cursor(std::shared_ptr<mg::CursorImage> const& /* initial_image */) -> std::shared_ptr<Cursor>
363 {
364- return std::weak_ptr<Cursor>();
365+ return std::shared_ptr<Cursor>();
366 }
367
368 std::unique_ptr<mg::GLContext> mga::AndroidDisplay::create_gl_context()
369
370=== modified file 'src/platform/graphics/android/android_display.h'
371--- src/platform/graphics/android/android_display.h 2014-04-09 09:28:17 +0000
372+++ src/platform/graphics/android/android_display.h 2014-04-14 21:12:09 +0000
373@@ -63,7 +63,7 @@
374 void pause();
375 void resume();
376
377- std::weak_ptr<Cursor> the_cursor();
378+ std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image);
379 std::unique_ptr<graphics::GLContext> create_gl_context();
380
381 private:
382
383=== modified file 'src/platform/graphics/mesa/cursor.cpp'
384--- src/platform/graphics/mesa/cursor.cpp 2014-03-06 06:05:17 +0000
385+++ src/platform/graphics/mesa/cursor.cpp 2014-04-14 21:12:09 +0000
386@@ -22,20 +22,21 @@
387 #include "kms_output_container.h"
388 #include "kms_display_configuration.h"
389 #include "mir/geometry/rectangle.h"
390+#include "mir/graphics/cursor_image.h"
391
392 #include <boost/exception/errinfo_errno.hpp>
393
394 #include <stdexcept>
395 #include <vector>
396
397-namespace mgm = mir::graphics::mesa;
398+namespace mg = mir::graphics;
399+namespace mgm = mg::mesa;
400 namespace geom = mir::geometry;
401
402 namespace
403 {
404-#include "black_arrow.c"
405-int const width = black_arrow.width;
406-int const height = black_arrow.height;
407+int const width = 64;
408+int const height = 64;
409
410 // Transforms a relative position within the display bounds described by \a rect which is rotated with \a orientation
411 geom::Displacement transform(geom::Rectangle const& rect, geom::Displacement const& vector, MirOrientation orientation)
412@@ -72,13 +73,14 @@
413 mgm::Cursor::Cursor(
414 gbm_device* gbm,
415 KMSOutputContainer& output_container,
416- std::shared_ptr<CurrentConfiguration> const& current_configuration) :
417+ std::shared_ptr<CurrentConfiguration> const& current_configuration,
418+ std::shared_ptr<mg::CursorImage> const& initial_image) :
419 output_container(output_container),
420 current_position(),
421 buffer(gbm),
422 current_configuration(current_configuration)
423 {
424- set_image(black_arrow.pixel_data, geometry::Size{width, height});
425+ set_image(initial_image);
426
427 show_at_last_known_position();
428 }
429@@ -88,14 +90,16 @@
430 hide();
431 }
432
433-void mgm::Cursor::set_image(const void* raw_argb, geometry::Size size)
434+void mgm::Cursor::set_image(std::shared_ptr<CursorImage const> const& cursor_image)
435 {
436+ auto const& size = cursor_image->size();
437+
438 if (size != geometry::Size{width, height})
439 BOOST_THROW_EXCEPTION(std::logic_error("No support for cursors that aren't 64x64"));
440
441 auto const count = size.width.as_uint32_t() * size.height.as_uint32_t() * sizeof(uint32_t);
442
443- if (auto result = gbm_bo_write(buffer, raw_argb, count))
444+ if (auto result = gbm_bo_write(buffer, cursor_image->as_argb_8888(), count))
445 {
446 BOOST_THROW_EXCEPTION(
447 ::boost::enable_error_info(std::runtime_error("failed to initialize gbm buffer"))
448
449=== modified file 'src/platform/graphics/mesa/cursor.h'
450--- src/platform/graphics/mesa/cursor.h 2014-03-06 06:05:17 +0000
451+++ src/platform/graphics/mesa/cursor.h 2014-04-14 21:12:09 +0000
452@@ -34,6 +34,8 @@
453 }
454 namespace graphics
455 {
456+class CursorImage;
457+
458 namespace mesa
459 {
460 class KMSOutputContainer;
461@@ -61,11 +63,12 @@
462 Cursor(
463 gbm_device* device,
464 KMSOutputContainer& output_container,
465- std::shared_ptr<CurrentConfiguration> const& current_configuration);
466+ std::shared_ptr<CurrentConfiguration> const& current_configuration,
467+ std::shared_ptr<CursorImage> const& cursor_image);
468
469 ~Cursor() noexcept;
470
471- void set_image(const void* raw_argb, geometry::Size size);
472+ void set_image(std::shared_ptr<CursorImage const> const& cursor_image);
473
474 void move_to(geometry::Point position);
475
476
477=== modified file 'src/platform/graphics/mesa/display.cpp'
478--- src/platform/graphics/mesa/display.cpp 2014-04-08 12:17:40 +0000
479+++ src/platform/graphics/mesa/display.cpp 2014-04-14 21:12:09 +0000
480@@ -224,7 +224,7 @@
481 clear_connected_unused_outputs();
482 }
483
484- if (cursor) cursor->show_at_last_known_position();
485+ if (auto c = cursor.lock()) c->show_at_last_known_position();
486 }
487
488 void mgm::Display::register_configuration_change_handler(
489@@ -255,7 +255,7 @@
490 {
491 try
492 {
493- if (cursor) cursor->hide();
494+ if (auto c = cursor.lock()) c->hide();
495 platform->drm.drop_master();
496 }
497 catch(std::runtime_error const& e)
498@@ -291,12 +291,16 @@
499 clear_connected_unused_outputs();
500 }
501
502- if (cursor) cursor->show_at_last_known_position();
503+ if (auto c = cursor.lock()) c->show_at_last_known_position();
504 }
505
506-auto mgm::Display::the_cursor() -> std::weak_ptr<graphics::Cursor>
507+auto mgm::Display::create_hardware_cursor(std::shared_ptr<mg::CursorImage> const& initial_image) -> std::shared_ptr<graphics::Cursor>
508 {
509- if (!cursor)
510+ // There is only one hardware cursor. We do not keep a strong reference to it in the display though,
511+ // if no other component of Mir is interested (i.e. the input stack does not keep a reference to send
512+ // position updates) we must be configured not to use a cursor and thusly let it deallocate.
513+ std::shared_ptr<mgm::Cursor> locked_cursor = cursor.lock();
514+ if (!locked_cursor)
515 {
516 class KMSCurrentConfiguration : public CurrentConfiguration
517 {
518@@ -317,11 +321,12 @@
519 Display& display;
520 };
521
522- cursor = std::make_shared<Cursor>(platform->gbm.device, output_container,
523- std::make_shared<KMSCurrentConfiguration>(*this));
524+ cursor = locked_cursor = std::make_shared<Cursor>(platform->gbm.device, output_container,
525+ std::make_shared<KMSCurrentConfiguration>(*this),
526+ initial_image);
527 }
528
529- return cursor;
530+ return locked_cursor;
531 }
532
533 std::unique_ptr<mg::GLContext> mgm::Display::create_gl_context()
534
535=== modified file 'src/platform/graphics/mesa/display.h'
536--- src/platform/graphics/mesa/display.h 2014-03-27 09:46:09 +0000
537+++ src/platform/graphics/mesa/display.h 2014-04-14 21:12:09 +0000
538@@ -79,7 +79,7 @@
539 void pause();
540 void resume();
541
542- std::weak_ptr<graphics::Cursor> the_cursor();
543+ std::shared_ptr<graphics::Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image);
544 std::unique_ptr<GLContext> create_gl_context();
545
546 private:
547@@ -93,7 +93,8 @@
548 std::vector<std::unique_ptr<DisplayBuffer>> display_buffers;
549 RealKMSOutputContainer output_container;
550 mutable RealKMSDisplayConfiguration current_display_configuration;
551- std::shared_ptr<Cursor> cursor;
552+
553+ std::weak_ptr<Cursor> cursor;
554 std::shared_ptr<GLConfig> const gl_config;
555 };
556
557
558=== modified file 'src/server/default_server_configuration.cpp'
559--- src/server/default_server_configuration.cpp 2014-04-07 10:23:17 +0000
560+++ src/server/default_server_configuration.cpp 2014-04-14 21:12:09 +0000
561@@ -91,25 +91,22 @@
562 {
563 struct DefaultCursorListener : mi::CursorListener
564 {
565- DefaultCursorListener(std::weak_ptr<mg::Cursor> const& cursor) :
566+ DefaultCursorListener(std::shared_ptr<mg::Cursor> const& cursor) :
567 cursor(cursor)
568 {
569 }
570
571 void cursor_moved_to(float abs_x, float abs_y)
572 {
573- if (auto c = cursor.lock())
574- {
575- c->move_to(geom::Point{abs_x, abs_y});
576- }
577+ cursor->move_to(geom::Point{abs_x, abs_y});
578 }
579
580- std::weak_ptr<mg::Cursor> const cursor;
581+ std::shared_ptr<mg::Cursor> const cursor;
582 };
583 return cursor_listener(
584 [this]() -> std::shared_ptr<mi::CursorListener>
585 {
586- return std::make_shared<DefaultCursorListener>(the_display()->the_cursor());
587+ return std::make_shared<DefaultCursorListener>(the_cursor());
588 });
589 }
590
591
592=== modified file 'src/server/graphics/CMakeLists.txt'
593--- src/server/graphics/CMakeLists.txt 2014-03-12 02:46:58 +0000
594+++ src/server/graphics/CMakeLists.txt 2014-04-14 21:12:09 +0000
595@@ -7,6 +7,7 @@
596 default_display_configuration_policy.cpp
597 gl_extensions_base.cpp
598 surfaceless_egl_context.cpp
599+ builtin_cursor_images.cpp
600 )
601
602 add_subdirectory(nested/)
603
604=== renamed file 'src/platform/graphics/mesa/black_arrow.c' => 'src/server/graphics/black_arrow.c'
605=== renamed file 'src/platform/graphics/mesa/black_arrow.xcf' => 'src/server/graphics/black_arrow.xcf'
606=== added file 'src/server/graphics/builtin_cursor_images.cpp'
607--- src/server/graphics/builtin_cursor_images.cpp 1970-01-01 00:00:00 +0000
608+++ src/server/graphics/builtin_cursor_images.cpp 2014-04-14 21:12:09 +0000
609@@ -0,0 +1,53 @@
610+/*
611+ * Copyright © 2014 Canonical Ltd.
612+ *
613+ * This program is free software: you can redistribute it and/or modify it
614+ * under the terms of the GNU General Public License version 3,
615+ * as published by the Free Software Foundation.
616+ *
617+ * This program is distributed in the hope that it will be useful,
618+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
619+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
620+ * GNU General Public License for more details.
621+ *
622+ * You should have received a copy of the GNU General Public License
623+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
624+ *
625+ * Authored by: Robert Carr <robert.carr@canonical.com>
626+ */
627+
628+#include "black_arrow.c"
629+#include "builtin_cursor_images.h"
630+
631+#include "mir/graphics/cursor_image.h"
632+
633+namespace mg = mir::graphics;
634+namespace geom = mir::geometry;
635+
636+namespace
637+{
638+struct BlackArrowCursorImage : public mg::CursorImage
639+{
640+ void const* as_argb_8888() const
641+ {
642+ return black_arrow.pixel_data;
643+ }
644+ geom::Size size() const
645+ {
646+ return { black_arrow.width, black_arrow.height };
647+ }
648+};
649+}
650+
651+mg::BuiltinCursorImages::BuiltinCursorImages()
652+ : builtin_image(std::make_shared<BlackArrowCursorImage>())
653+{
654+}
655+
656+std::shared_ptr<mg::CursorImage> mg::BuiltinCursorImages::image(std::string const& /* theme_name */,
657+ std::string const& /* cursor_name */,
658+ geom::Size const& /* size */)
659+{
660+ // Builtin repository only has one cursor and theme at a single size.
661+ return builtin_image;
662+}
663
664=== added file 'src/server/graphics/builtin_cursor_images.h'
665--- src/server/graphics/builtin_cursor_images.h 1970-01-01 00:00:00 +0000
666+++ src/server/graphics/builtin_cursor_images.h 2014-04-14 21:12:09 +0000
667@@ -0,0 +1,52 @@
668+/*
669+ * Copyright © 2014 Canonical Ltd.
670+ *
671+ * This program is free software: you can redistribute it and/or modify it
672+ * under the terms of the GNU General Public License version 3,
673+ * as published by the Free Software Foundation.
674+ *
675+ * This program is distributed in the hope that it will be useful,
676+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
677+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
678+ * GNU General Public License for more details.
679+ *
680+ * You should have received a copy of the GNU General Public License
681+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
682+ *
683+ * Authored by: Robert Carr <robert.carr@canonical.com>
684+ */
685+
686+
687+#ifndef MIR_GRAPHICS_BUILTIN_CURSOR_LOADER_H_
688+#define MIR_GRAPHICS_BUILTIN_CURSOR_LOADER_H_
689+
690+#include "mir/graphics/cursor_images.h"
691+
692+namespace mir
693+{
694+namespace graphics
695+{
696+class CursorImage;
697+
698+class BuiltinCursorImages : public CursorImages
699+{
700+public:
701+ BuiltinCursorImages();
702+ virtual ~BuiltinCursorImages() = default;
703+
704+ std::shared_ptr<CursorImage> image(std::string const& theme_name,
705+ std::string const& cursor_name,
706+ geometry::Size const& size);
707+
708+protected:
709+ BuiltinCursorImages(BuiltinCursorImages const&) = delete;
710+ BuiltinCursorImages& operator=(BuiltinCursorImages const&) = delete;
711+
712+private:
713+ std::shared_ptr<CursorImage> const builtin_image;
714+};
715+}
716+}
717+
718+
719+#endif /* MIR_GRAPHICS_BUILTIN_CURSOR_LOADER_H_ */
720
721=== modified file 'src/server/graphics/default_configuration.cpp'
722--- src/server/graphics/default_configuration.cpp 2014-03-27 09:52:04 +0000
723+++ src/server/graphics/default_configuration.cpp 2014-04-14 21:12:09 +0000
724@@ -34,6 +34,8 @@
725
726 #include <boost/throw_exception.hpp>
727
728+#include "builtin_cursor_images.h"
729+
730 #include <map>
731
732 namespace mg = mir::graphics;
733@@ -114,6 +116,39 @@
734 });
735 }
736
737+std::shared_ptr<mg::Cursor>
738+mir::DefaultServerConfiguration::the_cursor()
739+{
740+ return cursor(
741+ [this]() -> std::shared_ptr<mg::Cursor>
742+ {
743+ // For now we only support a hardware cursor.
744+ return the_display()->create_hardware_cursor(the_default_cursor_image());
745+ });
746+}
747+
748+std::shared_ptr<mg::CursorImage>
749+mir::DefaultServerConfiguration::the_default_cursor_image()
750+{
751+ static geometry::Size const default_cursor_size = {geometry::Width{64},
752+ geometry::Height{64}};
753+ return default_cursor_image(
754+ [this]()
755+ {
756+ return the_cursor_images()->image("default", "arrow", default_cursor_size);
757+ });
758+}
759+
760+std::shared_ptr<mg::CursorImages>
761+mir::DefaultServerConfiguration::the_cursor_images()
762+{
763+ return cursor_images(
764+ [this]()
765+ {
766+ return std::make_shared<mg::BuiltinCursorImages>();
767+ });
768+}
769+
770 auto mir::DefaultServerConfiguration::the_host_connection()
771 -> std::shared_ptr<graphics::nested::HostConnection>
772 {
773
774=== modified file 'src/server/graphics/nested/nested_display.cpp'
775--- src/server/graphics/nested/nested_display.cpp 2014-04-02 08:20:24 +0000
776+++ src/server/graphics/nested/nested_display.cpp 2014-04-14 21:12:09 +0000
777@@ -288,10 +288,10 @@
778 // TODO If we "own" the cursor then we need to restore it
779 }
780
781-auto mgn::NestedDisplay::the_cursor()->std::weak_ptr<Cursor>
782+auto mgn::NestedDisplay::create_hardware_cursor(std::shared_ptr<mg::CursorImage> const& /* initial image */)->std::shared_ptr<Cursor>
783 {
784 // TODO Do we "own" the cursor or does the host mir?
785- return std::weak_ptr<Cursor>();
786+ return std::shared_ptr<Cursor>();
787 }
788
789 std::unique_ptr<mg::GLContext> mgn::NestedDisplay::create_gl_context()
790
791=== modified file 'src/server/graphics/nested/nested_display.h'
792--- src/server/graphics/nested/nested_display.h 2014-04-02 08:20:24 +0000
793+++ src/server/graphics/nested/nested_display.h 2014-04-14 21:12:09 +0000
794@@ -121,7 +121,7 @@
795 void pause() override;
796 void resume() override;
797
798- std::weak_ptr<Cursor> the_cursor() override;
799+ std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image) override;
800 std::unique_ptr<graphics::GLContext> create_gl_context() override;
801
802 private:
803
804=== modified file 'src/server/graphics/offscreen/display.cpp'
805--- src/server/graphics/offscreen/display.cpp 2014-03-26 05:48:59 +0000
806+++ src/server/graphics/offscreen/display.cpp 2014-04-14 21:12:09 +0000
807@@ -163,7 +163,7 @@
808 {
809 }
810
811-std::weak_ptr<mg::Cursor> mgo::Display::the_cursor()
812+std::shared_ptr<mg::Cursor> mgo::Display::create_hardware_cursor(std::shared_ptr<mg::CursorImage> const& /* initial_image */)
813 {
814 return {};
815 }
816
817=== modified file 'src/server/graphics/offscreen/display.h'
818--- src/server/graphics/offscreen/display.h 2014-01-31 07:48:24 +0000
819+++ src/server/graphics/offscreen/display.h 2014-04-14 21:12:09 +0000
820@@ -86,7 +86,7 @@
821 void pause();
822 void resume();
823
824- std::weak_ptr<Cursor> the_cursor();
825+ std::shared_ptr<Cursor> create_hardware_cursor(std::shared_ptr<CursorImage> const& initial_image);
826 std::unique_ptr<GLContext> create_gl_context();
827
828 private:
829
830=== modified file 'tests/unit-tests/graphics/mesa/test_cursor.cpp'
831--- tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-03-26 05:48:59 +0000
832+++ tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-04-14 21:12:09 +0000
833@@ -21,6 +21,8 @@
834 #include "src/platform/graphics/mesa/kms_output_container.h"
835 #include "src/platform/graphics/mesa/kms_display_configuration.h"
836
837+#include "mir/graphics/cursor_image.h"
838+
839 #include "mir_test_doubles/mock_gbm.h"
840 #include "mir_test/fake_shared.h"
841 #include "mock_kms_output.h"
842@@ -31,11 +33,14 @@
843 #include <unordered_map>
844 #include <algorithm>
845
846+#include <string.h>
847+
848 namespace mg = mir::graphics;
849 namespace mgm = mir::graphics::mesa;
850 namespace geom = mir::geometry;
851-namespace mtd = mir::test::doubles;
852-using mir::test::MockKMSOutput;
853+namespace mt = mir::test;
854+namespace mtd = mt::doubles;
855+using mt::MockKMSOutput;
856
857 namespace
858 {
859@@ -196,15 +201,31 @@
860 StubKMSDisplayConfiguration conf;
861 };
862
863+struct StubCursorImage : public mg::CursorImage
864+{
865+ void const* as_argb_8888() const
866+ {
867+ return image_data;
868+ }
869+ geom::Size size() const
870+ {
871+ return geom::Size{geom::Width{64}, geom::Height{64}};
872+ }
873+ static void const* image_data;
874+};
875+void const* StubCursorImage::image_data = reinterpret_cast<void*>(&StubCursorImage::image_data);
876+
877 struct MesaCursorTest : public ::testing::Test
878 {
879 MesaCursorTest()
880 : cursor{mock_gbm.fake_gbm.device, output_container,
881- mir::test::fake_shared(current_configuration)}
882+ mt::fake_shared(current_configuration),
883+ mt::fake_shared(stub_image)}
884 {
885 }
886
887 StubCurrentConfiguration current_configuration;
888+ StubCursorImage stub_image;
889 testing::NiceMock<mtd::MockGBM> mock_gbm;
890 StubKMSOutputContainer output_container;
891 mgm::Cursor cursor;
892@@ -221,33 +242,40 @@
893 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE));
894
895 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
896- std::make_shared<StubCurrentConfiguration>()};
897+ std::make_shared<StubCurrentConfiguration>(),
898+ std::make_shared<StubCursorImage>()};
899 }
900
901 TEST_F(MesaCursorTest, set_cursor_writes_to_bo)
902 {
903 using namespace testing;
904
905- void* const image{reinterpret_cast<void*>(0x5678)};
906+ StubCursorImage image;
907 size_t const cursor_side{64};
908 geom::Size const cursor_size{cursor_side, cursor_side};
909 size_t const cursor_size_bytes{cursor_side * cursor_side * sizeof(uint32_t)};
910
911- EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, image, cursor_size_bytes));
912+ EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, StubCursorImage::image_data, cursor_size_bytes));
913
914- cursor.set_image(image, cursor_size);
915+ cursor.set_image(mt::fake_shared(image));
916 }
917
918 TEST_F(MesaCursorTest, set_cursor_throws_on_incorrect_size)
919 {
920 using namespace testing;
921
922- void* const image{reinterpret_cast<void*>(0x5678)};
923- size_t const cursor_side{48};
924- geom::Size const cursor_size{cursor_side, cursor_side};
925+ struct InvalidlySizedCursorImage : public StubCursorImage
926+ {
927+ geom::Size size() const
928+ {
929+ return invalid_cursor_size;
930+ }
931+ size_t const cursor_side{48};
932+ geom::Size const invalid_cursor_size{cursor_side, cursor_side};
933+ };
934
935 EXPECT_THROW(
936- cursor.set_image(image, cursor_size);
937+ cursor.set_image(std::make_shared<InvalidlySizedCursorImage>());
938 , std::logic_error);
939 }
940
941@@ -266,7 +294,8 @@
942 EXPECT_CALL(*output_container.outputs[12], has_cursor()).Times(0);
943
944 mgm::Cursor cursor_tmp{mock_gbm.fake_gbm.device, output_container,
945- std::make_shared<StubCurrentConfiguration>()};
946+ std::make_shared<StubCurrentConfiguration>(),
947+ std::make_shared<StubCursorImage>()};
948
949 output_container.verify_and_clear_expectations();
950 }

Subscribers

People subscribed via source and target branches