Merge lp:~larsu/libindicator/add-indicator-ng into lp:libindicator/13.04

Proposed by Lars Karlitski
Status: Merged
Approved by: Charles Kerr
Approved revision: 505
Merged at revision: 475
Proposed branch: lp:~larsu/libindicator/add-indicator-ng
Merge into: lp:libindicator/13.04
Diff against target: 1278 lines (+1078/-15)
17 files modified
Makefile.am.coverage (+3/-1)
configure.ac (+1/-1)
libindicator/Makefile.am (+8/-0)
libindicator/indicator-image-helper.c (+20/-4)
libindicator/indicator-image-helper.h (+5/-3)
libindicator/indicator-ng.c (+566/-0)
libindicator/indicator-ng.h (+48/-0)
libindicator/indicator-object.c (+7/-0)
libindicator/indicator-object.h (+1/-0)
tests/Makefile.am (+42/-1)
tests/com.canonical.indicator.test.service.in (+3/-0)
tests/com.canonical.test.indicator (+4/-0)
tests/com.canonical.test.nosuchservice.indicator (+4/-0)
tests/indicator-test-service.c (+108/-0)
tests/test-indicator-ng.c (+167/-0)
tools/indicator-loader.c (+22/-5)
trim-lcov.py (+69/-0)
To merge this branch: bzr merge lp:~larsu/libindicator/add-indicator-ng
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Charles Kerr (community) Approve
Ted Gould (community) Approve
Review via email: mp+143934@code.launchpad.net

Description of the change

Add IndicatorNg.

IndicatorNg is an indicator object that reads an indicator service file and watches the bus for a corresponding service to appear. It turns the menus and actions exported by the service into an indicator entry. I think this is a good solution for the transition period in which we support both styles of indicators. (It means we don't need to copy templates around.)

An indicator service file must have an ".indicator" extension and contents simlilar to this:

  [Indicator Service]
  Name=indicator-test
  BusName=com.canonical.indicator.test
  ObjectPath=/com/canonical/indicator/test

For unity-panel-service, these files will be installed somewhere. The indicator-loader in this branch accepts a path to such a file as the first command line argument (instead of the .so file).

This can be tested with the example indicator in lp:~larsu/libunity/add-indicator (examples/indicator.vala).

To post a comment you must log in.
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: Needs Fixing (continuous-integration)
478. By Lars Karlitski

indicator-ng.h: use local include

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
479. By Lars Karlitski

indicator-ng: always set an accessible description to avoid imminent warning

480. By Lars Karlitski

indicator-ng: fix crash (tried to free a string with g_object_unref)

481. By Lars Karlitski

indicator-ng: add indicator_ng_new_for_profile

482. By Lars Karlitski

indicator-ng: add getters

483. By Lars Karlitski

Make sure indicator-ng.h is installed

484. By Lars Karlitski

Add basic tests for indicator-ng

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
485. By Lars Karlitski

indicator-ng: properly unset action group when the service disappears

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
486. By Lars Karlitski

indicator-ng: auto start service if it's not running

487. By Lars Karlitski

indicator-ng: use base->get_entries to get the invisible ones, too

488. By Lars Karlitski

indicator-ng: set name hint to the value of the service file's "Name" field

489. By Lars Karlitski

indicator-ng: more elaborate testing

Use GTestDBus to spawn a small test service (tests/indicator-test-service.c)
and check whether the indicator menu gets turned into a gtkmenu correctly.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

1. You're overriding get_label, get_image, get_entry, get_name, get_accessible... and also have that indicator_ng_get_entry() func to work around visible/invisible entries in the parent. It would be more straightforward to remove all of that, replace the label/image/gtkmenu/accessible_desc fields in _IndicatorNg with "IndicatorObjectEntry entry;" and override get_entries() as a one-liner "return g_list_append (NULL, &self->entry);"

2. Bonus points if you'd add "void indicator_object_entry_set_accessible_desc (IndicatorObject * io, IndicatorObjectEntry * entry, const char * accessible_desc);" in indicator-object.h, similar to the existing i_o_e_activate() func, and use that instead of indicator_ng_set_accessible_desc()

3. I'm wondering if we should use a 'broken' image as a fallback when setting the image. That would prevent the "disappearing indicator" problem from regressing, such as we had with indicator-session when the cog icon wasn't in the theme. A broken icon isn't ideal, but it's better than no icon at all. Same for the gtk_widget_hide() call in indicator_ng_update_entry().

4. is there a reason to not use (&s&s&sb) in indicator_ng_update_entry's g_variant_get() call?

5. "added < 2 && removed < 2 && added ^ removed" deserves a comment in the source code. I /think/ you're XORing to confirm that added and removed aren't the same. Whether or not that's the case, it deserves a code comment.

6. It might be slightly cleaner to use g_object_connect() in indicator_ng_service_appeared(), and g_object_disconnect(). This isn't an important point.

7. In indicator_ng_menu_changed(), we may use "action" as an uninitialized pointer if g_menu_model_get_item_attribute() fails. This seems unlikely to me, but it would be better to check for failure and log a g_warning() instead of using an uninitialized pointer.

8. g_build_filename() instead of g_strconcat()?

Revision history for this message
Charles Kerr (charlesk) wrote :

NVM about g_build_filename(); that doesn't make sense for object paths.

490. By Lars Karlitski

indicator-ng: test indicator_ng_get_property

491. By Lars Karlitski

Add trim-lcov.py

It strips some unreachable branches from the coverage reports, such as
g_return_if_fail.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
492. By Lars Karlitski

indicator-ng: use an IndicatorObjectEntry internally

We needed the entry anyway (that's what indicator_ng_get_entry was for).

493. By Lars Karlitski

indicator-ng: show broken image when g_icon_for_string returns NULL

494. By Lars Karlitski

indicator-ng: save unnecessary allocations by using "&" in g_variant_get

495. By Lars Karlitski

indicator-ng: document error conditions in menu_changed

496. By Lars Karlitski

indicator-ng: check return value of g_menu_model_get_item_attribute

If it returns false, we'd use uninitialized memory.

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (3.2 KiB)

> 1. You're overriding get_label, get_image, get_entry, get_name,
> get_accessible... and also have that indicator_ng_get_entry() func to work
> around visible/invisible entries in the parent. It would be more
> straightforward to remove all of that, replace the
> label/image/gtkmenu/accessible_desc fields in _IndicatorNg with
> "IndicatorObjectEntry entry;" and override get_entries() as a one-liner
> "return g_list_append (NULL, &self->entry);"

Very good point, thank you.

 1 file changed, 29 insertions(+), 86 deletions(-)

r492.

> 2. Bonus points if you'd add "void indicator_object_entry_set_accessible_desc
> (IndicatorObject * io, IndicatorObjectEntry * entry, const char *
> accessible_desc);" in indicator-object.h, similar to the existing
> i_o_e_activate() func, and use that instead of
> indicator_ng_set_accessible_desc()

Makes sense, but I'm not sure this works without potentially breaking some code, because accessible_desc is declared as const. That's the reason I still kept an accessible_desc in the IndicatorNg struct.

Or am I missing something?

> 3. I'm wondering if we should use a 'broken' image as a fallback when setting
> the image. That would prevent the "disappearing indicator" problem from
> regressing, such as we had with indicator-session when the cog icon wasn't in
> the theme. A broken icon isn't ideal, but it's better than no icon at all.
> Same for the gtk_widget_hide() call in indicator_ng_update_entry().

I totally agree and this is the way I intended it to work. The icon string we get falls into one of these cases:

 (1) an empty string
 (2) a valid icon name, for which an icon is available on the system
 (3) a valid icon name without an installed icon
 (4) a malformed icon string (i.e. g_icon_new_for_string() returns NULL)

For (1), no icon should be displayed. (2) obviously shows the installed icon. Both (3) and (4) should display the broken image. I already did this for (3) (set_from_gicon_string() returns TRUE, but GtkImage sets a broken icon), but I missed case (4).

Fixed in r493.

> 4. is there a reason to not use (&s&s&sb) in indicator_ng_update_entry's
> g_variant_get() call?

No, fixed in r494.

> 5. "added < 2 && removed < 2 && added ^ removed" deserves a comment in the
> source code. I /think/ you're XORing to confirm that added and removed aren't
> the same. Whether or not that's the case, it deserves a code comment.

Very true. r495.

> 6. It might be slightly cleaner to use g_object_connect() in
> indicator_ng_service_appeared(), and g_object_disconnect(). This isn't an
> important point.

Yeah, I dislike that call when dealing with swapped arguments.

> 7. In indicator_ng_menu_changed(), we may use "action" as an uninitialized
> pointer if g_menu_model_get_item_attribute() fails. This seems unlikely to me,
> but it would be better to check for failure and log a g_warning() instead of
> using an uninitialized pointer.

Good catch! I don't think it makes sense to throw a warning here. Even if the menu item has an "action" attribute, the value might not point to a valid action.

Fixed in r496.

> 8. g_build_filename() instead of g_strconcat()?

That's an object path, not a filename.

Thank...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :
Download full text (3.6 KiB)

* When the service vanishes the program is immediately hiding the indicator and destroying everything. It doesn't make much sense to show this error to the user unless it can't be restarted. It should try to restart, and if either a timeout happens or an error on restart happens it hides the indicator. Much better user experience if we can handle the error in a reasonable way.

* I don't understand that, if we only have one action group, the action names need the "indicator" prefix. It seems they could just be unprefixed and make it easier on everyone.

* In indicator_ng_initable_init() you should really check for the group before looking at individual values in the group. It makes the errors much better for people trying to figure out WTH is going on when they mess up the file.

* In indicator_ng_initable_init() it seems like name and the bus name check should be separate, they're pretty independent and again it'll help error reporting.

* It looks like the name that is gotten from the Key file is not going through translations, which it needs to as it is a user visible string. This also means that the indicator file will need to give the translation domain.

* I don't understand why the profile is a string and not an enum. It seems like there'll always be a limited set, and they can't overlap, so enum is the right type to use. Seems like this should be passed up to the "environments" of indicator-object.

* We probably need a way for a .indicator file to support multiple profiles in the same file. It doesn't really make sense to have a whole set of files since all of them will have to be loaded for the convergence story.

* Having a single action that has a state (sssb) doesn't make a lot of sense as it makes it nearly impossible for us to remove or add attributes later. Better to split it into 4 today so that it can be 5 (or 3) tomorrow.

* The indicator needs a way to provide a string that can be used for measuring the space needed. We provide that in the libappindicator API and we should honor it further up the stack.

* If I remember right gtk_image_set_from_gicon() only supports icons that are square. Not all of our icons are square. Icons like the power icon are rectangular.

* You shouldn't use math on string pointers. This is bad "g_strdup (action + 10)". The problem is that if someone changes "indicator." to "bob." they'll grep through the code and never see that. Then they'll introduce a bug. If you do strlen() on a constant string the compiler can optimize that, and you still can explain the meaning of what you're intention is.

* Overall there are *very* few comments in the code.

* In the new interfacing there is no way to check the version of the service. The scenario would be that an upgrade happens, and the service no longer matches the version that the panel-service thinks it's looking for. It needs to handle that error instead of displaying erroneous results or an empty menu. I'd recommend adding this to the indicator file format and as an attribute on the base menu.

* It seems like the about-to-show needs to be passed down to the indicator somehow. This isn't currently handled and it's required for ...

Read more...

review: Needs Fixing
497. By Lars Karlitski

indicator-ng: require header item to have x-canonical-type set

498. By Lars Karlitski

indicator-ng: lazily allocate entry.label and entry.image

Most indicators only need one of those.

499. By Lars Karlitski

indicator-ng: use indicator_image_helper

gtk_icon_set_from_gicon doesn't scale rectangular icons correctly.

This adds indicator_image_helper_update_from_gicon() and makes refresh_image()
set a broken image instead of erroring out when an icon couldn't be found.

500. By Lars Karlitski

indicator-ng: use strlen instead of hard coding the length

501. By Lars Karlitski

trim-lcov.py: add license header

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (6.5 KiB)

Thanks a lot for the detailed review!

> * When the service vanishes the program is immediately hiding the indicator
> and destroying everything. It doesn't make much sense to show this error to
> the user unless it can't be restarted. It should try to restart, and if
> either a timeout happens or an error on restart happens it hides the
> indicator. Much better user experience if we can handle the error in a
> reasonable way.

Right, this isn't true for all indicators though. How about a "auto-reload" property that is set for all built-in indicators (or even settable from the .indicator file)?

> * I don't understand that, if we only have one action group, the action names
> need the "indicator" prefix. It seems they could just be unprefixed and make
> it easier on everyone.

I want to enable applications to reuse their app. actions in an indicator. Without this distinction, apps would have to add the same actions twice.

> * In indicator_ng_initable_init() you should really check for the group before
> looking at individual values in the group. It makes the errors much better
> for people trying to figure out WTH is going on when they mess up the file.

Good point. I started to implement it when I realised that g_key_file_get_string() already returns an error when the group doesn't exist:

  could not load indicator from <filename>: Key file does not have group 'Indicator Service'

I don't know what else a special check could add.

> * In indicator_ng_initable_init() it seems like name and the bus name check
> should be separate, they're pretty independent and again it'll help error
> reporting.

On second look, this is indeed a bit convoluted. But I'm not sure what you mean: move the g_bus_watch_name call out of the condition, or out of the function? It doesn't have any error conditions (I admit the "return self->name_watch_id > 0" is a bit misleading).

> * It looks like the name that is gotten from the Key file is not going through
> translations, which it needs to as it is a user visible string. This also
> means that the indicator file will need to give the translation domain.

The name from the key file the name-hint really, which isn't user-visible, is it? Would it be clearer if we named the key "NameHint" instead?

> * I don't understand why the profile is a string and not an enum. It seems
> like there'll always be a limited set, and they can't overlap, so enum is the
> right type to use. Seems like this should be passed up to the "environments"
> of indicator-object.

Oh! I meant to look into "environments", but forgot. Maybe we don't even need a separate "profile" property at all.

How exactly do these work? Does unity-panrl-service already create an indicator-object for each "environment" (greeter, desktop)?

> * We probably need a way for a .indicator file to support multiple profiles in
> the same file. It doesn't really make sense to have a whole set of files
> since all of them will have to be loaded for the convergence story.

That's already the way it works: each indicator-service can expose as many profiles as it wants to on <objectpath>/<profile>, where <objectpath> is from the .indicator file.

> ...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :
Download full text (7.2 KiB)

On Fri, 2013-01-25 at 10:58 +0000, Lars Uebernickel wrote:
> > * When the service vanishes the program is immediately hiding the indicator
> > and destroying everything. It doesn't make much sense to show this error to
> > the user unless it can't be restarted. It should try to restart, and if
> > either a timeout happens or an error on restart happens it hides the
> > indicator. Much better user experience if we can handle the error in a
> > reasonable way.
>
> Right, this isn't true for all indicators though. How about a
> "auto-reload" property that is set for all built-in indicators (or
> even settable from the .indicator file)?

Uhm, which ones don't? That would be bad if there were some that don't.
Remember that we only want to use this for system indicators, not
applications. Because we need to wait to display the panel until all of
the services that have .indicator files are started and giving us
default state. We can't assume that for appindicators.

> > * I don't understand that, if we only have one action group, the action names
> > need the "indicator" prefix. It seems they could just be unprefixed and make
> > it easier on everyone.
>
> I want to enable applications to reuse their app. actions in an
> indicator. Without this distinction, apps would have to add the same
> actions twice.

Are you thinking appindicators here? I don't think we can drop KSNI
there, so we won't be able to use this for that. Otherwise all
indicators don't have any application actions.

Really application actions will probably only be used long term to
support GNOME applications on the desktop. They've shown no desire to
work with us (and KDE) on indicators in the past, I doubt they will in
the future. So never the two shall meet :-)

> > * In indicator_ng_initable_init() you should really check for the group before
> > looking at individual values in the group. It makes the errors much better
> > for people trying to figure out WTH is going on when they mess up the file.
>
> Good point. I started to implement it when I realised that
> g_key_file_get_string() already returns an error when the group
> doesn't exist:
>
> could not load indicator from <filename>: Key file does not have group 'Indicator Service'
>
> I don't know what else a special check could add.

Ah, okay. That should be fine then. Didn't realize it gave that error.

> > * In indicator_ng_initable_init() it seems like name and the bus name check
> > should be separate, they're pretty independent and again it'll help error
> > reporting.
>
> On second look, this is indeed a bit convoluted. But I'm not sure
> what you mean: move the g_bus_watch_name call out of the condition, or
> out of the function? It doesn't have any error conditions (I admit
> the "return self->name_watch_id > 0" is a bit misleading).

I was more thinking of breaking up this if statement into to:

515 + if ((self->name = g_key_file_get_string (keyfile, "Indicator
Service", "Name", error)) &&
516 + (bus_name = g_key_file_get_string (keyfile, "Indicator Service",
"BusName", error)) &&
517 + (self->object_path = g_key_file_get_string (keyfile, "Indicator
Service", "ObjectPath", error)))

It seems ...

Read more...

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (5.3 KiB)

> > Right, this isn't true for all indicators though. How about a
> > "auto-reload" property that is set for all built-in indicators (or
> > even settable from the .indicator file)?
>
> Uhm, which ones don't? That would be bad if there were some that don't.
> Remember that we only want to use this for system indicators, not
> applications. Because we need to wait to display the panel until all of
> the services that have .indicator files are started and giving us
> default state. We can't assume that for appindicators.

Matthew is thinking about having the sync indicator only show up when it is syncing. The (now deprecated) print indicator only shows when printing. Similar for keyboard / bluetooth, alhough they would probably only hide their indicators instead of terminating the service.

I definitely want to use the same mechanism for appindicators.

> > I want to enable applications to reuse their app. actions in an
> > indicator. Without this distinction, apps would have to add the same
> > actions twice.
>
> Are you thinking appindicators here? I don't think we can drop KSNI
> there, so we won't be able to use this for that. Otherwise all
> indicators don't have any application actions.

An application having an indicator is pretty common. I think it would be much easier for them if the could just hook up existing actions to the indicator menu, in the same way that they hook up those actions to the application menu.

I'm not saying we should drop support for KSNI. Though I think that spec needs to be updated (it depends on dbusmenu, doesn't it?)

> Really application actions will probably only be used long term to
> support GNOME applications on the desktop. They've shown no desire to
> work with us (and KDE) on indicators in the past, I doubt they will in
> the future. So never the two shall meet :-)

Hm? Why would a $TOOLKIT application not have application actions? I thought application menus were part of our platform?

What do you mean by GNOME applications? Apps using GNOME tech? Lots of those use indicators and go out of their way to support the Unity desktop (tomboy, transmission, shutter, sparkleshare). Also note that even though the indicator concept wasn't picked up by GNOME's default applications, there are features in GTK+ and GMenuModel that are *only* used in Unity's indicators.

I'm really just trying to make it as easy as possible for application developers to add an indicator to their app and hook it up to existing actions (see also my libunity merge request).

> [...]
> > On second look, this is indeed a bit convoluted. But I'm not sure
> > what you mean: move the g_bus_watch_name call out of the condition, or
> > out of the function? It doesn't have any error conditions (I admit
> > the "return self->name_watch_id > 0" is a bit misleading).
>
> I was more thinking of breaking up this if statement into to:
>
> 515 + if ((self->name = g_key_file_get_string (keyfile, "Indicator
> Service", "Name", error)) &&
> 516 + (bus_name = g_key_file_get_string (keyfile, "Indicator Service",
> "BusName", error)) &&
> 517 + (self->object_path = g_key_file_get_string (keyfile, "Indicator
> Service",...

Read more...

Revision history for this message
Ted Gould (ted) wrote :
Download full text (6.0 KiB)

On Mon, 2013-01-28 at 10:27 +0000, Lars Uebernickel wrote:
> > > Right, this isn't true for all indicators though. How about a
> > > "auto-reload" property that is set for all built-in indicators (or
> > > even settable from the .indicator file)?
> >
> > Uhm, which ones don't? That would be bad if there were some that don't.
> > Remember that we only want to use this for system indicators, not
> > applications. Because we need to wait to display the panel until all of
> > the services that have .indicator files are started and giving us
> > default state. We can't assume that for appindicators.
>
> Matthew is thinking about having the sync indicator only show up when
> it is syncing. The (now deprecated) print indicator only shows when
> printing. Similar for keyboard � bluetooth, alhough they would
> probably only hide their indicators instead of terminating the
> service.

Sure. Though, it would be nice if they could terminate the service...
it seems like that's largely a special case rather than the standard
though. Seems like default should be auto-reload.

> I definitely want to use the same mechanism for appindicators.

Hmm, so then we'd need another property for which ones should be waited
on for initial bring up. And perhaps a way to mark KSNI ones as
there'll be different properties. I think that we're a ways away from
that.

But, in general, I'm not sure if using the same mechanism makes sense.
This can be a lot simpler and cleaner if it doesn't have to take on all
of the KSNI interface as well. Remember it is a registration interface,
so we wouldn't be able to really *replace* it, it'd have to be
additional and we'd have to start the service, then wait for the
registration.

> > > I want to enable applications to reuse their app. actions in an
> > > indicator. Without this distinction, apps would have to add the same
> > > actions twice.
> >
> > Are you thinking appindicators here? I don't think we can drop KSNI
> > there, so we won't be able to use this for that. Otherwise all
> > indicators don't have any application actions.
>
> An application having an indicator is pretty common. I think it would
> be much easier for them if the could just hook up existing actions to
> the indicator menu, in the same way that they hook up those actions to
> the application menu.

I'd challenge the initial assumption. I can think of very few
applications that have indicators, and I'd go further to say that most
that do are annoying :-)

I have no issue with them hooking up actions to the indicator. But it
would probably make more sense to allow registering action groups and
prefixes in general. As most applications don't have app�win actions,
but might have their own scheme for such a separation. For instance, I
think for Inkscape it'd make much more sense to have app�doc�win.
Unfortunately GtkApplication isn't very flexible, we shouldn't make the
same mistake.

> I'm not saying we should drop support for KSNI. Though I think that
> spec needs to be updated (it depends on dbusmenu, doesn't it?)

It just provides a path for a menu. I think if we ported qt-sni and
libappindicator there'd be no issue with those being GMenu ba...

Read more...

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (7.3 KiB)

> On Mon, 2013-01-28 at 10:27 +0000, Lars Uebernickel wrote:
> > > > Right, this isn't true for all indicators though. How about a
> > > > "auto-reload" property that is set for all built-in indicators (or
> > > > even settable from the .indicator file)?
> > >
> > > Uhm, which ones don't? That would be bad if there were some that don't.
> > > Remember that we only want to use this for system indicators, not
> > > applications. Because we need to wait to display the panel until all of
> > > the services that have .indicator files are started and giving us
> > > default state. We can't assume that for appindicators.
> >
> > Matthew is thinking about having the sync indicator only show up when
> > it is syncing. The (now deprecated) print indicator only shows when
> > printing. Similar for keyboard � bluetooth, alhough they would
> > probably only hide their indicators instead of terminating the
> > service.
>
> Sure. Though, it would be nice if they could terminate the service...
> it seems like that's largely a special case rather than the standard
> though. Seems like default should be auto-reload.

The default for system indicator should be auto-reload, I agree. I still think having a key in the .indicator file is a good idea.

> > I definitely want to use the same mechanism for appindicators.
>
> Hmm, so then we'd need another property for which ones should be waited
> on for initial bring up. And perhaps a way to mark KSNI ones as
> there'll be different properties. I think that we're a ways away from
> that.
>
> But, in general, I'm not sure if using the same mechanism makes sense.
> This can be a lot simpler and cleaner if it doesn't have to take on all
> of the KSNI interface as well. Remember it is a registration interface,
> so we wouldn't be able to really *replace* it, it'd have to be
> additional and we'd have to start the service, then wait for the
> registration.

Let's talk about this in more detail later. Or is it important for this MR?

> > > > I want to enable applications to reuse their app. actions in an
> > > > indicator. Without this distinction, apps would have to add the same
> > > > actions twice.
> > >
> > > Are you thinking appindicators here? I don't think we can drop KSNI
> > > there, so we won't be able to use this for that. Otherwise all
> > > indicators don't have any application actions.
> >
> > An application having an indicator is pretty common. I think it would
> > be much easier for them if the could just hook up existing actions to
> > the indicator menu, in the same way that they hook up those actions to
> > the application menu.
>
> I'd challenge the initial assumption. I can think of very few
> applications that have indicators, and I'd go further to say that most
> that do are annoying :-)

Really? Even if there are only a few, doesn't it make sense to allow them to use app. actions? I don't think sticking indicator. in front is such a big deal really.

> I have no issue with them hooking up actions to the indicator. But it
> would probably make more sense to allow registering action groups and
> prefixes in general. As most applications don't have app�win actions,
> but might...

Read more...

502. By Lars Karlitski

indicator-ng: simplify flow in initable_init

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
503. By Lars Karlitski

indicator-ng: try to restart the service when it crashes

This uses a (slightly) awkward heuristic: when the well-known name vanishes
from the session bus, it only restarts the service when it didn't explicitly
hide the indicator before.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
504. By Lars Karlitski

indicator-ng: don't hide the indicator if the service is already running

Revision history for this message
Ted Gould (ted) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
505. By Lars Karlitski

indicator-ng: add license header

Revision history for this message
Charles Kerr (charlesk) wrote :

Thanks for that update :)

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile.am.coverage'
--- Makefile.am.coverage 2012-03-27 21:33:31 +0000
+++ Makefile.am.coverage 2013-02-14 21:19:21 +0000
@@ -1,6 +1,8 @@
11
2# Coverage targets2# Coverage targets
33
4EXTRA_DIST = trim-lcov.py
5
4.PHONY: clean-gcno clean-gcda \6.PHONY: clean-gcno clean-gcda \
5 coverage-html generate-coverage-html clean-coverage-html \7 coverage-html generate-coverage-html clean-coverage-html \
6 coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr8 coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr
@@ -23,7 +25,7 @@
23 25
24generate-coverage-html:26generate-coverage-html:
25 @echo Collecting coverage data27 @echo Collecting coverage data
26 $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool28 $(LCOV) --directory $(top_builddir) --capture --no-checksum --compat-libtool | $(top_srcdir)/trim-lcov.py > coverage.info
27 LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info29 LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
28 30
29clean-coverage-html: clean-gcda31clean-coverage-html: clean-gcda
3032
=== modified file 'configure.ac'
--- configure.ac 2012-11-21 19:21:10 +0000
+++ configure.ac 2013-02-14 21:19:21 +0000
@@ -43,7 +43,7 @@
43##############################43##############################
4444
45GTK_REQUIRED_VERSION=2.1845GTK_REQUIRED_VERSION=2.18
46GTK3_REQUIRED_VERSION=2.9146GTK3_REQUIRED_VERSION=3.6
47GIO_UNIX_REQUIRED_VERSION=2.2247GIO_UNIX_REQUIRED_VERSION=2.22
4848
49AC_ARG_WITH([gtk],49AC_ARG_WITH([gtk],
5050
=== modified file 'libindicator/Makefile.am'
--- libindicator/Makefile.am 2012-01-24 16:10:15 +0000
+++ libindicator/Makefile.am 2013-02-14 21:19:21 +0000
@@ -36,6 +36,10 @@
36 indicator-service.h \36 indicator-service.h \
37 indicator-service-manager.h37 indicator-service-manager.h
3838
39if USE_GTK3
40indicator_headers += indicator-ng.h
41endif
42
39libindicatorinclude_HEADERS = \43libindicatorinclude_HEADERS = \
40 $(indicator_headers)44 $(indicator_headers)
4145
@@ -53,6 +57,10 @@
53 indicator-service.c \57 indicator-service.c \
54 indicator-service-manager.c58 indicator-service-manager.c
5559
60if USE_GTK3
61libindicator_la_SOURCES += indicator-ng.c
62endif
63
56libindicator_la_CFLAGS = \64libindicator_la_CFLAGS = \
57 $(LIBINDICATOR_CFLAGS) \65 $(LIBINDICATOR_CFLAGS) \
58 $(COVERAGE_CFLAGS) \66 $(COVERAGE_CFLAGS) \
5967
=== modified file 'libindicator/indicator-image-helper.c'
--- libindicator/indicator-image-helper.c 2012-04-11 06:01:51 +0000
+++ libindicator/indicator-image-helper.c 2013-02-14 21:19:21 +0000
@@ -62,7 +62,12 @@
62 /* Grab the filename */62 /* Grab the filename */
63 icon_filename = gtk_icon_info_get_filename(icon_info);63 icon_filename = gtk_icon_info_get_filename(icon_info);
64 }64 }
65 g_return_if_fail(icon_filename != NULL); /* An error because we don't have a filename */65
66 /* show a broken image if we don't have a filename */
67 if (icon_filename == NULL) {
68 gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR);
69 return;
70 }
6671
67 /* Build a pixbuf */72 /* Build a pixbuf */
68 GError * error = NULL;73 GError * error = NULL;
@@ -132,7 +137,8 @@
132 /* Build us an image */137 /* Build us an image */
133 GtkImage * image = GTK_IMAGE(gtk_image_new());138 GtkImage * image = GTK_IMAGE(gtk_image_new());
134139
135 indicator_image_helper_update(image, name);140 if (name)
141 indicator_image_helper_update(image, name);
136142
137 return image;143 return image;
138}144}
@@ -144,17 +150,27 @@
144 g_return_if_fail(name != NULL);150 g_return_if_fail(name != NULL);
145 g_return_if_fail(name[0] != '\0');151 g_return_if_fail(name[0] != '\0');
146 g_return_if_fail(GTK_IS_IMAGE(image));152 g_return_if_fail(GTK_IS_IMAGE(image));
147 gboolean seen_previously = FALSE;
148153
149 /* Build us a GIcon */154 /* Build us a GIcon */
150 GIcon * icon_names = g_themed_icon_new_with_default_fallbacks(name);155 GIcon * icon_names = g_themed_icon_new_with_default_fallbacks(name);
151 g_warn_if_fail(icon_names != NULL);156 g_warn_if_fail(icon_names != NULL);
152 g_return_if_fail(icon_names != NULL);157 g_return_if_fail(icon_names != NULL);
153158
159 indicator_image_helper_update_from_gicon (image, icon_names);
160
161 g_object_unref (icon_names);
162 return;
163}
164
165void
166indicator_image_helper_update_from_gicon (GtkImage *image, GIcon *icon)
167{
168 gboolean seen_previously = FALSE;
169
154 seen_previously = (g_object_get_data(G_OBJECT(image), INDICATOR_NAMES_DATA) != NULL);170 seen_previously = (g_object_get_data(G_OBJECT(image), INDICATOR_NAMES_DATA) != NULL);
155171
156 /* Attach our names to the image */172 /* Attach our names to the image */
157 g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, icon_names, g_object_unref);173 g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, g_object_ref (icon), g_object_unref);
158174
159 /* Put the pixbuf in */175 /* Put the pixbuf in */
160 refresh_image(image);176 refresh_image(image);
161177
=== modified file 'libindicator/indicator-image-helper.h'
--- libindicator/indicator-image-helper.h 2010-03-11 16:22:17 +0000
+++ libindicator/indicator-image-helper.h 2013-02-14 21:19:21 +0000
@@ -26,8 +26,10 @@
2626
27#include <gtk/gtk.h>27#include <gtk/gtk.h>
2828
29GtkImage * indicator_image_helper (const gchar * name);29GtkImage * indicator_image_helper (const gchar * name);
30void indicator_image_helper_update (GtkImage * image,30void indicator_image_helper_update (GtkImage * image,
31 const gchar * name);31 const gchar * name);
32void indicator_image_helper_update_from_gicon (GtkImage * image,
33 GIcon * icon);
3234
33#endif /* __INDICATOR_IMAGE_HELPER_H__ */35#endif /* __INDICATOR_IMAGE_HELPER_H__ */
3436
=== added file 'libindicator/indicator-ng.c'
--- libindicator/indicator-ng.c 1970-01-01 00:00:00 +0000
+++ libindicator/indicator-ng.c 2013-02-14 21:19:21 +0000
@@ -0,0 +1,566 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lars Uebernickel <lars.uebernickel@canonical.com>
18 */
19
20#include "indicator-ng.h"
21#include "indicator-image-helper.h"
22
23#include <string.h>
24
25struct _IndicatorNg
26{
27 IndicatorObject parent;
28
29 gchar *service_file;
30 gchar *name;
31 gchar *object_path;
32 gchar *bus_name;
33 gchar *profile;
34 gchar *header_action;
35
36 guint name_watch_id;
37
38 GDBusConnection *session_bus;
39 GActionGroup *actions;
40 GMenuModel *menu;
41
42 IndicatorObjectEntry entry;
43 gchar *accessible_desc;
44
45 gint64 last_service_restart;
46};
47
48static void indicator_ng_initable_iface_init (GInitableIface *initable);
49G_DEFINE_TYPE_WITH_CODE (IndicatorNg, indicator_ng, INDICATOR_OBJECT_TYPE,
50 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, indicator_ng_initable_iface_init))
51
52enum
53{
54 PROP_0,
55 PROP_SERVICE_FILE,
56 PROP_PROFILE,
57 N_PROPERTIES
58};
59
60static GParamSpec *properties[N_PROPERTIES];
61
62static void
63indicator_ng_get_property (GObject *object,
64 guint property_id,
65 GValue *value,
66 GParamSpec *pspec)
67{
68 IndicatorNg *self = INDICATOR_NG (object);
69
70 switch (property_id)
71 {
72 case PROP_SERVICE_FILE:
73 g_value_set_string (value, self->service_file);
74 break;
75
76 case PROP_PROFILE:
77 g_value_set_string (value, self->profile);
78 break;
79
80 default:
81 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82 }
83}
84
85static void
86indicator_ng_set_property (GObject *object,
87 guint property_id,
88 const GValue *value,
89 GParamSpec *pspec)
90{
91 IndicatorNg *self = INDICATOR_NG (object);
92
93 switch (property_id)
94 {
95 case PROP_SERVICE_FILE: /* construct-only */
96 self->service_file = g_strdup (g_value_get_string (value));
97 break;
98
99 case PROP_PROFILE: /* construct-only */
100 self->profile = g_strdup (g_value_get_string (value));
101 break;
102
103 default:
104 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
105 }
106}
107
108static void
109indicator_ng_free_actions_and_menu (IndicatorNg *self)
110{
111 if (self->actions)
112 {
113 gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", NULL);
114 g_signal_handlers_disconnect_by_data (self->actions, self);
115 g_clear_object (&self->actions);
116 }
117
118 if (self->menu)
119 {
120 g_signal_handlers_disconnect_by_data (self->menu, self);
121 g_clear_object (&self->menu);
122 }
123}
124
125static void
126indicator_ng_dispose (GObject *object)
127{
128 IndicatorNg *self = INDICATOR_NG (object);
129
130 if (self->name_watch_id)
131 {
132 g_bus_unwatch_name (self->name_watch_id);
133 self->name_watch_id = 0;
134 }
135
136 g_clear_object (&self->session_bus);
137
138 indicator_ng_free_actions_and_menu (self);
139
140 g_clear_object (&self->entry.label);
141 g_clear_object (&self->entry.image);
142 g_clear_object (&self->entry.menu);
143
144 G_OBJECT_CLASS (indicator_ng_parent_class)->dispose (object);
145}
146
147static void
148indicator_ng_finalize (GObject *object)
149{
150 IndicatorNg *self = INDICATOR_NG (object);
151
152 g_free (self->service_file);
153 g_free (self->name);
154 g_free (self->object_path);
155 g_free (self->bus_name);
156 g_free (self->accessible_desc);
157 g_free (self->header_action);
158
159 G_OBJECT_CLASS (indicator_ng_parent_class)->finalize (object);
160}
161
162static GList *
163indicator_ng_get_entries (IndicatorObject *io)
164{
165 IndicatorNg *self = INDICATOR_NG (io);
166
167 return g_list_append (NULL, &self->entry);
168}
169
170static void
171indicator_ng_set_accessible_desc (IndicatorNg *self,
172 const gchar *accessible_desc)
173{
174 g_free (self->accessible_desc);
175 self->accessible_desc = g_strdup (accessible_desc);
176
177 self->entry.accessible_desc = self->accessible_desc;
178 g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, &self->entry);
179}
180
181static void
182indicator_ng_set_icon_from_string (IndicatorNg *self,
183 const gchar *str)
184{
185 GIcon *icon;
186 GError *error = NULL;
187
188 if (str == NULL || *str == '\0')
189 {
190 if (self->entry.image)
191 {
192 gtk_image_clear (self->entry.image);
193 gtk_widget_hide (GTK_WIDGET (self->entry.image));
194 }
195 return;
196 }
197
198 if (!self->entry.image)
199 self->entry.image = g_object_ref_sink (gtk_image_new ());
200
201 gtk_widget_show (GTK_WIDGET (self->entry.image));
202
203 icon = g_icon_new_for_string (str, &error);
204 if (icon)
205 {
206 indicator_image_helper_update_from_gicon (self->entry.image, icon);
207 g_object_unref (icon);
208 }
209 else
210 {
211 g_warning ("invalid icon string '%s': %s", str, error->message);
212 gtk_image_set_from_stock (self->entry.image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR);
213 g_error_free (error);
214 }
215}
216
217static void
218indicator_ng_set_label (IndicatorNg *self,
219 const gchar *label)
220{
221 if (label == NULL || *label == '\0')
222 {
223 if (self->entry.label)
224 gtk_widget_hide (GTK_WIDGET (self->entry.label));
225 return;
226 }
227
228 if (!self->entry.label)
229 self->entry.label = g_object_ref_sink (gtk_label_new (NULL));
230
231 gtk_label_set_label (GTK_LABEL (self->entry.label), label);
232 gtk_widget_show (GTK_WIDGET (self->entry.label));
233}
234
235static void
236indicator_ng_update_entry (IndicatorNg *self)
237{
238 GVariant *state;
239
240 g_return_if_fail (self->menu != NULL);
241 g_return_if_fail (self->actions != NULL);
242
243 if (!self->header_action ||
244 !g_action_group_has_action (self->actions, self->header_action))
245 {
246 indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
247 return;
248 }
249
250 state = g_action_group_get_action_state (self->actions, self->header_action);
251 if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)")))
252 {
253 const gchar *label;
254 const gchar *iconstr;
255 const gchar *accessible_desc;
256 gboolean visible;
257
258 g_variant_get (state, "(&s&s&sb)", &label, &iconstr, &accessible_desc, &visible);
259
260 indicator_ng_set_label (self, label);
261 indicator_ng_set_icon_from_string (self, iconstr);
262 indicator_ng_set_accessible_desc (self, accessible_desc);
263 indicator_object_set_visible (INDICATOR_OBJECT (self), visible);
264 }
265 else
266 g_warning ("the action of the indicator menu item must have state with type (sssb)");
267
268 if (state)
269 g_variant_unref (state);
270}
271
272static gboolean
273indicator_ng_menu_item_is_of_type (GMenuModel *menu,
274 gint index,
275 const gchar *expected_type)
276{
277 gchar *type;
278 gboolean has_type = FALSE;
279
280 if (g_menu_model_get_item_attribute (menu, index, "x-canonical-type", "s", &type))
281 {
282 has_type = g_str_equal (type, expected_type);
283 g_free (type);
284 }
285
286 return has_type;
287}
288
289static void
290indicator_ng_menu_changed (GMenuModel *menu,
291 gint position,
292 gint removed,
293 gint added,
294 gpointer user_data)
295{
296 IndicatorNg *self = user_data;
297
298 /* The menu may only contain one item (the indicator title menu).
299 * Thus, the position is always 0, and there is either exactly one
300 * item added or exactly one item removed.
301 */
302 g_return_if_fail (position == 0);
303 g_return_if_fail (added < 2 && removed < 2 && added ^ removed);
304
305 if (removed)
306 indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
307
308 if (added)
309 {
310 g_clear_pointer (&self->header_action, g_free);
311
312 if (indicator_ng_menu_item_is_of_type (self->menu, 0, "com.canonical.indicator.root"))
313 {
314 GMenuModel *popup;
315 gchar *action;
316
317 if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) &&
318 g_str_has_prefix (action, "indicator."))
319 {
320 self->header_action = g_strdup (action + strlen ("indicator."));
321 }
322
323 popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU);
324 if (popup)
325 {
326 gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE);
327 g_object_unref (popup);
328 }
329
330 indicator_ng_update_entry (self);
331
332 g_free (action);
333 }
334 else
335 g_warning ("indicator menu item must be of type 'com.canonical.indicator.root'");
336 }
337}
338
339static void
340indicator_ng_service_appeared (GDBusConnection *connection,
341 const gchar *name,
342 const gchar *name_owner,
343 gpointer user_data)
344{
345 IndicatorNg *self = user_data;
346 gchar *menu_object_path;
347
348 g_assert (!self->actions);
349 g_assert (!self->menu);
350
351 self->session_bus = g_object_ref (connection);
352
353 self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path));
354 gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", self->actions);
355 g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self);
356 g_signal_connect_swapped (self->actions, "action-removed", G_CALLBACK (indicator_ng_update_entry), self);
357 g_signal_connect_swapped (self->actions, "action-state-changed", G_CALLBACK (indicator_ng_update_entry), self);
358
359 menu_object_path = g_strconcat (self->object_path, "/", self->profile, NULL);
360 self->menu = G_MENU_MODEL (g_dbus_menu_model_get (connection, name_owner, menu_object_path));
361 g_signal_connect (self->menu, "items-changed", G_CALLBACK (indicator_ng_menu_changed), self);
362 if (g_menu_model_get_n_items (self->menu))
363 indicator_ng_menu_changed (self->menu, 0, 0, 1, self);
364
365 indicator_ng_update_entry (self);
366
367 g_free (menu_object_path);
368}
369
370static void
371indicator_ng_service_started (GObject *source_object,
372 GAsyncResult *result,
373 gpointer user_data)
374{
375 IndicatorNg *self = user_data;
376 GError *error = NULL;
377 GVariant *reply;
378
379 reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error);
380 if (!reply)
381 {
382 g_warning ("Could not activate service '%s': %s", self->name, error->message);
383 indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
384 g_error_free (error);
385 return;
386 }
387
388 switch (g_variant_get_uint32 (reply))
389 {
390 case 1: /* DBUS_START_REPLY_SUCCESS */
391 break;
392
393 case 2: /* DBUS_START_REPLY_ALREADY_RUNNING */
394 g_warning ("could not start service '%s': it is already running", self->name);
395 break;
396
397 default:
398 g_assert_not_reached ();
399 }
400
401 g_variant_unref (reply);
402}
403
404static void
405indicator_ng_service_vanished (GDBusConnection *connection,
406 const gchar *name,
407 gpointer user_data)
408{
409 IndicatorNg *self = user_data;
410
411 indicator_ng_free_actions_and_menu (self);
412
413 /* Names may vanish because the service decided it doesn't need to
414 * show its indicator anymore, or because it crashed. Let's assume it
415 * crashes and restart it unless it explicitly hid its indicator. */
416
417 if (indicator_object_entry_is_visible (INDICATOR_OBJECT (self), &self->entry))
418 {
419 gint64 now;
420
421 /* take care not to start it if it repeatedly crashes */
422 now = g_get_monotonic_time ();
423 if (now - self->last_service_restart < 1 * G_USEC_PER_SEC)
424 return;
425
426 self->last_service_restart = now;
427
428 g_dbus_connection_call (self->session_bus,
429 "org.freedesktop.DBus",
430 "/",
431 "org.freedesktop.DBus",
432 "StartServiceByName",
433 g_variant_new ("(su)", self->bus_name, 0),
434 G_VARIANT_TYPE ("(u)"),
435 G_DBUS_CALL_FLAGS_NONE,
436 -1,
437 NULL,
438 indicator_ng_service_started,
439 self);
440 }
441}
442
443static gboolean
444indicator_ng_initable_init (GInitable *initable,
445 GCancellable *cancellable,
446 GError **error)
447{
448 IndicatorNg *self = INDICATOR_NG (initable);
449 GKeyFile *keyfile;
450
451 keyfile = g_key_file_new ();
452 if (!g_key_file_load_from_file (keyfile, self->service_file, G_KEY_FILE_NONE, error))
453 goto out;
454
455 if (!(self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error)))
456 goto out;
457
458 self->entry.name_hint = self->name;
459
460 if (!(self->bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)))
461 goto out;
462
463 if (!(self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error)))
464 goto out;
465
466 self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
467 self->bus_name,
468 G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
469 indicator_ng_service_appeared,
470 indicator_ng_service_vanished,
471 self, NULL);
472
473out:
474 g_key_file_free (keyfile);
475
476 return self->name_watch_id > 0;
477}
478
479static void
480indicator_ng_class_init (IndicatorNgClass *class)
481{
482 GObjectClass *object_class = G_OBJECT_CLASS (class);
483 IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS (class);
484
485 object_class->get_property = indicator_ng_get_property;
486 object_class->set_property = indicator_ng_set_property;
487 object_class->dispose = indicator_ng_dispose;
488 object_class->finalize = indicator_ng_finalize;
489
490 io_class->get_entries = indicator_ng_get_entries;
491
492 properties[PROP_SERVICE_FILE] = g_param_spec_string ("service-file",
493 "Service file",
494 "Path of the service file",
495 NULL,
496 G_PARAM_READWRITE |
497 G_PARAM_CONSTRUCT_ONLY |
498 G_PARAM_STATIC_STRINGS);
499
500 properties[PROP_PROFILE] = g_param_spec_string ("profile",
501 "Profile",
502 "Indicator profile",
503 "desktop",
504 G_PARAM_READWRITE |
505 G_PARAM_CONSTRUCT_ONLY |
506 G_PARAM_STATIC_STRINGS);
507
508 g_object_class_install_properties(object_class, N_PROPERTIES, properties);
509}
510
511static void
512indicator_ng_initable_iface_init (GInitableIface *initable)
513{
514 initable->init = indicator_ng_initable_init;
515}
516
517static void
518indicator_ng_init (IndicatorNg *self)
519{
520 self->entry.menu = g_object_ref_sink (gtk_menu_new ());
521
522 /* work around IndicatorObject's warning that the accessible
523 * description is missing. We never set it on construction, but when
524 * the menu model has arrived on the bus.
525 */
526 self->accessible_desc = g_strdup ("");
527 self->entry.accessible_desc = self->accessible_desc;
528
529 indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
530}
531
532IndicatorNg *
533indicator_ng_new (const gchar *service_file,
534 GError **error)
535{
536 return g_initable_new (INDICATOR_TYPE_NG, NULL, error,
537 "service-file", service_file,
538 NULL);
539}
540
541IndicatorNg *
542indicator_ng_new_for_profile (const gchar *service_file,
543 const gchar *profile,
544 GError **error)
545{
546 return g_initable_new (INDICATOR_TYPE_NG, NULL, error,
547 "service-file", service_file,
548 "profile", profile,
549 NULL);
550}
551
552const gchar *
553indicator_ng_get_service_file (IndicatorNg *self)
554{
555 g_return_val_if_fail (INDICATOR_IS_NG (self), NULL);
556
557 return self->service_file;
558}
559
560const gchar *
561indicator_ng_get_profile (IndicatorNg *self)
562{
563 g_return_val_if_fail (INDICATOR_IS_NG (self), NULL);
564
565 return self->profile;
566}
0567
=== added file 'libindicator/indicator-ng.h'
--- libindicator/indicator-ng.h 1970-01-01 00:00:00 +0000
+++ libindicator/indicator-ng.h 2013-02-14 21:19:21 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3, as published
6 * by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranties of
10 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lars Uebernickel <lars.uebernickel@canonical.com>
18 */
19
20#ifndef __INDICATOR_NG_H__
21#define __INDICATOR_NG_H__
22
23#include "indicator-object.h"
24
25#define INDICATOR_TYPE_NG (indicator_ng_get_type ())
26#define INDICATOR_NG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_NG, IndicatorNg))
27#define INDICATOR_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_TYPE_NG, IndicatorNgClass))
28#define INDICATOR_IS_NG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_NG))
29#define INDICATOR_IS_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_TYPE_NG))
30#define INDICATOR_NG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_TYPE_NG, IndicatorNgClass))
31
32typedef struct _IndicatorNg IndicatorNg;
33typedef IndicatorObjectClass IndicatorNgClass;
34
35GType indicator_ng_get_type (void);
36
37IndicatorNg * indicator_ng_new (const gchar *service_file,
38 GError **error);
39
40IndicatorNg * indicator_ng_new_for_profile (const gchar *service_file,
41 const gchar *profile,
42 GError **error);
43
44const gchar * indicator_ng_get_service_file (IndicatorNg *indicator);
45
46const gchar * indicator_ng_get_profile (IndicatorNg *indicator);
47
48#endif
049
=== modified file 'libindicator/indicator-object.c'
--- libindicator/indicator-object.c 2012-09-19 20:00:57 +0000
+++ libindicator/indicator-object.c 2013-02-14 21:19:21 +0000
@@ -935,3 +935,10 @@
935 }935 }
936}936}
937937
938gboolean
939indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry)
940{
941 g_return_val_if_fail (INDICATOR_IS_OBJECT (io), FALSE);
942
943 return entry_get_private (io, entry)->visibility == ENTRY_VISIBLE;
944}
938945
=== modified file 'libindicator/indicator-object.h'
--- libindicator/indicator-object.h 2012-02-23 11:28:01 +0000
+++ libindicator/indicator-object.h 2013-02-14 21:19:21 +0000
@@ -199,6 +199,7 @@
199guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry);199guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry);
200guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry);200guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry);
201void indicator_object_set_visible (IndicatorObject * io, gboolean visible);201void indicator_object_set_visible (IndicatorObject * io, gboolean visible);
202gboolean indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry);
202void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);203void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);
203void indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp);204void indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp);
204void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);205void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);
205206
=== modified file 'tests/Makefile.am'
--- tests/Makefile.am 2012-06-18 18:42:53 +0000
+++ tests/Makefile.am 2013-02-14 21:19:21 +0000
@@ -26,7 +26,8 @@
26 session.conf.in \26 session.conf.in \
27 service-manager-connect.service.in \27 service-manager-connect.service.in \
28 service-version-bad.service.in \28 service-version-bad.service.in \
29 service-version-good.service.in29 service-version-good.service.in \
30 com.canonical.indicator.test.service.in
3031
31#############################32#############################
32# Test Loader33# Test Loader
@@ -466,3 +467,43 @@
466467
467DISTCLEANFILES += $(XML_REPORT) $(HTML_REPORT)468DISTCLEANFILES += $(XML_REPORT) $(HTML_REPORT)
468469
470#############################
471# Indicator-ng
472#############################
473
474if USE_GTK3
475
476com.canonical.indicator.test.service: com.canonical.indicator.test.service.in Makefile.am
477 sed -e "s|\@builddir\@|$(abspath $(builddir))|" $< > $@
478
479check_PROGRAMS += test-indicator-ng
480
481test_indicator_ng_SOURCES = test-indicator-ng.c
482
483test_indicator_ng_CFLAGS = \
484 $(LIBINDICATOR_CFLAGS) \
485 -I$(top_srcdir) \
486 -DSRCDIR="\"$(srcdir)\"" \
487 -DBUILD_DIR="\"$(abs_builddir)\"" \
488 -Wall
489
490test_indicator_ng_LDADD = \
491 $(LIBINDICATOR_LIBS) \
492 -L$(top_builddir)/libindicator/.libs \
493 $(INDICATOR_LIB)
494
495TESTS += test-indicator-ng
496DISTCLEANFILES += test-indicator-ng
497
498# Mock indicator service
499check_PROGRAMS += indicator-test-service
500
501indicator_test_service_SOURCES = indicator-test-service.c
502indicator_test_service_CFLAGS = $(LIBINDICATOR_CFLAGS)
503indicator_test_service_LDADD = $(LIBINDICATOR_LIBS)
504
505EXTRA_indicator_test_service_DEPENDENCIES = com.canonical.indicator.test.service
506
507DISTCLEANFILES += indicator-test-service com.canonical.indicator.test.service
508
509endif
469510
=== added file 'tests/com.canonical.indicator.test.service.in'
--- tests/com.canonical.indicator.test.service.in 1970-01-01 00:00:00 +0000
+++ tests/com.canonical.indicator.test.service.in 2013-02-14 21:19:21 +0000
@@ -0,0 +1,3 @@
1[D-BUS Service]
2Name=com.canonical.indicator.test
3Exec=@builddir@/indicator-test-service
04
=== added file 'tests/com.canonical.test.indicator'
--- tests/com.canonical.test.indicator 1970-01-01 00:00:00 +0000
+++ tests/com.canonical.test.indicator 2013-02-14 21:19:21 +0000
@@ -0,0 +1,4 @@
1[Indicator Service]
2Name=indicator-test
3BusName=com.canonical.indicator.test
4ObjectPath=/com/canonical/indicator/test
05
=== added file 'tests/com.canonical.test.nosuchservice.indicator'
--- tests/com.canonical.test.nosuchservice.indicator 1970-01-01 00:00:00 +0000
+++ tests/com.canonical.test.nosuchservice.indicator 2013-02-14 21:19:21 +0000
@@ -0,0 +1,4 @@
1[Indicator Service]
2Name=indicator-test
3BusName=com.canonical.indicator.test.nosuchservice
4ObjectPath=/com/canonical/indicator/test
05
=== added file 'tests/indicator-test-service.c'
--- tests/indicator-test-service.c 1970-01-01 00:00:00 +0000
+++ tests/indicator-test-service.c 2013-02-14 21:19:21 +0000
@@ -0,0 +1,108 @@
1
2#include <gio/gio.h>
3
4typedef struct
5{
6 GSimpleActionGroup *actions;
7 GMenu *menu;
8
9 guint actions_export_id;
10 guint menu_export_id;
11} IndicatorTestService;
12
13static void
14bus_acquired (GDBusConnection *connection,
15 const gchar *name,
16 gpointer user_data)
17{
18 IndicatorTestService *indicator = user_data;
19 GError *error = NULL;
20
21 indicator->actions_export_id = g_dbus_connection_export_action_group (connection,
22 "/com/canonical/indicator/test",
23 G_ACTION_GROUP (indicator->actions),
24 &error);
25 if (indicator->actions_export_id == 0)
26 {
27 g_warning ("cannot export action group: %s", error->message);
28 g_error_free (error);
29 return;
30 }
31
32 indicator->menu_export_id = g_dbus_connection_export_menu_model (connection,
33 "/com/canonical/indicator/test/desktop",
34 G_MENU_MODEL (indicator->menu),
35 &error);
36 if (indicator->menu_export_id == 0)
37 {
38 g_warning ("cannot export menu: %s", error->message);
39 g_error_free (error);
40 return;
41 }
42}
43
44static void
45name_lost (GDBusConnection *connection,
46 const gchar *name,
47 gpointer user_data)
48{
49 IndicatorTestService *indicator = user_data;
50
51 if (indicator->actions_export_id)
52 g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id);
53
54 if (indicator->menu_export_id)
55 g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id);
56}
57
58static void
59activate_show (GSimpleAction *action,
60 GVariant *parameter,
61 gpointer user_data)
62{
63 g_message ("showing");
64}
65
66int
67main (int argc, char **argv)
68{
69 IndicatorTestService indicator = { 0 };
70 GMenuItem *item;
71 GMenu *submenu;
72 GActionEntry entries[] = {
73 { "_header", NULL, NULL, "('Test', 'indicator-test', 'Test indicator', true)", NULL },
74 { "show", activate_show, NULL, NULL, NULL }
75 };
76 GMainLoop *loop;
77
78 indicator.actions = g_simple_action_group_new ();
79 g_simple_action_group_add_entries (indicator.actions, entries, G_N_ELEMENTS (entries), NULL);
80
81 submenu = g_menu_new ();
82 g_menu_append (submenu, "Show", "indicator.show");
83 item = g_menu_item_new (NULL, "indicator._header");
84 g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root");
85 g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
86 indicator.menu = g_menu_new ();
87 g_menu_append_item (indicator.menu, item);
88
89 g_bus_own_name (G_BUS_TYPE_SESSION,
90 "com.canonical.indicator.test",
91 G_BUS_NAME_OWNER_FLAGS_NONE,
92 bus_acquired,
93 NULL,
94 name_lost,
95 &indicator,
96 NULL);
97
98 loop = g_main_loop_new (NULL, FALSE);
99 g_main_loop_run (loop);
100
101 g_object_unref (submenu);
102 g_object_unref (item);
103 g_object_unref (indicator.actions);
104 g_object_unref (indicator.menu);
105 g_object_unref (loop);
106
107 return 0;
108}
0109
=== added file 'tests/test-indicator-ng.c'
--- tests/test-indicator-ng.c 1970-01-01 00:00:00 +0000
+++ tests/test-indicator-ng.c 2013-02-14 21:19:21 +0000
@@ -0,0 +1,167 @@
1
2#include <libindicator/indicator-ng.h>
3
4static void
5indicator_ng_test_func (gconstpointer user_data)
6{
7 GTestFunc test_func = user_data;
8 GTestDBus *bus;
9
10 bus = g_test_dbus_new (G_TEST_DBUS_NONE);
11 g_test_dbus_add_service_dir (bus, BUILD_DIR);
12 g_test_dbus_up (bus);
13
14 test_func ();
15
16 g_test_dbus_down (bus);
17 g_object_unref (bus);
18}
19
20#define indicator_ng_test_add(name, test_func) \
21 g_test_add_data_func ("/indicator-ng/" name, test_func, indicator_ng_test_func)
22
23static gboolean
24stop_main_loop (gpointer user_data)
25{
26 GMainLoop *loop = user_data;
27
28 g_main_loop_quit (loop);
29
30 return FALSE;
31}
32
33static void
34test_non_existing (void)
35{
36 IndicatorNg *indicator;
37 GError *error = NULL;
38
39 indicator = indicator_ng_new (SRCDIR "/com.canonical.does.not.exist.indicator", &error);
40 g_assert (indicator == NULL);
41 g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
42
43 g_clear_error (&error);
44}
45
46static void
47test_instantiation (void)
48{
49 IndicatorNg *indicator;
50 GError *error = NULL;
51 GMainLoop *loop;
52
53 indicator = indicator_ng_new (SRCDIR "/com.canonical.test.nosuchservice.indicator", &error);
54 g_assert (indicator);
55 g_assert (error == NULL);
56
57 g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.nosuchservice.indicator");
58 g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "desktop");
59
60 {
61 gchar *service_file;
62 gchar *profile;
63
64 g_object_get (indicator, "service-file", &service_file,
65 "profile", &profile,
66 NULL);
67
68 g_assert_cmpstr (service_file, ==, SRCDIR "/com.canonical.test.nosuchservice.indicator");
69 g_assert_cmpstr (profile, ==, "desktop");
70
71 g_free (service_file);
72 g_free (profile);
73 }
74
75 loop = g_main_loop_new (NULL, FALSE);
76 g_timeout_add (200, stop_main_loop, loop);
77 g_main_loop_run (loop);
78
79 /* no such service, so there shouldn't be any indicators */
80 g_assert (indicator_object_get_entries (INDICATOR_OBJECT (indicator)) == NULL);
81
82 g_main_loop_unref (loop);
83 g_object_unref (indicator);
84}
85
86static void
87test_instantiation_with_profile (void)
88{
89 IndicatorNg *indicator;
90 GError *error = NULL;
91
92 indicator = indicator_ng_new_for_profile (SRCDIR "/com.canonical.test.indicator", "greeter", &error);
93 g_assert (indicator);
94 g_assert (error == NULL);
95
96 g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "greeter");
97
98 g_object_unref (indicator);
99}
100
101static void
102test_menu (void)
103{
104 IndicatorNg *indicator;
105 GError *error = NULL;
106 GMainLoop *loop;
107 GList *entries;
108 IndicatorObjectEntry *entry;
109
110 indicator = indicator_ng_new (SRCDIR "/com.canonical.test.indicator", &error);
111 g_assert (indicator);
112 g_assert (error == NULL);
113
114 loop = g_main_loop_new (NULL, FALSE);
115 g_timeout_add (500, stop_main_loop, loop);
116 g_main_loop_run (loop);
117
118 entries = indicator_object_get_entries (INDICATOR_OBJECT (indicator));
119 g_assert_cmpint (g_list_length (entries), ==, 1);
120
121 entry = entries->data;
122 g_assert_cmpstr (entry->name_hint, ==, "indicator-test");
123 g_assert_cmpstr (entry->accessible_desc, ==, "Test indicator");
124 g_assert_cmpstr (gtk_label_get_label (entry->label), ==, "Test");
125 g_assert (gtk_image_get_storage_type (entry->image) == GTK_IMAGE_STOCK);
126 {
127 GList *children;
128 GtkMenuItem *item;
129
130 g_assert (entry->menu != NULL);
131
132 children = gtk_container_get_children (GTK_CONTAINER (entry->menu));
133 g_assert_cmpint (g_list_length (children), ==, 1);
134
135 item = children->data;
136 g_assert (GTK_IS_MENU_ITEM (item));
137 g_assert (gtk_widget_is_sensitive (GTK_WIDGET (item)));
138 g_assert_cmpstr (gtk_menu_item_get_label (item), ==, "Show");
139
140 g_list_free (children);
141 }
142
143 g_list_free (entries);
144 g_main_loop_unref (loop);
145 g_object_unref (indicator);
146}
147
148int
149main (int argc, char **argv)
150{
151 /* gvfs, dconf, and appmenu-gtk leak GDbusConnections, which confuses
152 * g_test_dbus_down. Make sure we're not using any of those.
153 */
154 g_setenv ("GIO_USE_VFS", "local", TRUE);
155 g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
156 g_unsetenv ("UBUNTU_MENUPROXY");
157
158 g_test_init (&argc, &argv, NULL);
159 gtk_init (&argc, &argv);
160
161 indicator_ng_test_add ("non-existing", test_non_existing);
162 indicator_ng_test_add ("instantiation", test_instantiation);
163 indicator_ng_test_add ("instantiation-with-profile", test_instantiation_with_profile);
164 indicator_ng_test_add ("menu", test_menu);
165
166 return g_test_run ();
167}
0168
=== modified file 'tools/indicator-loader.c'
--- tools/indicator-loader.c 2012-09-19 20:00:57 +0000
+++ tools/indicator-loader.c 2013-02-14 21:19:21 +0000
@@ -25,6 +25,10 @@
25#include <gtk/gtk.h>25#include <gtk/gtk.h>
26#include <libindicator/indicator-object.h>26#include <libindicator/indicator-object.h>
2727
28#if GTK_MAJOR_VERSION == 3
29#include <libindicator/indicator-ng.h>
30#endif
31
28static GHashTable * entry_to_menuitem = NULL;32static GHashTable * entry_to_menuitem = NULL;
2933
30#define ENTRY_DATA_NAME "indicator-custom-entry-data"34#define ENTRY_DATA_NAME "indicator-custom-entry-data"
@@ -129,14 +133,27 @@
129 g_debug("Looking at Module: %s", name);133 g_debug("Looking at Module: %s", name);
130 g_return_val_if_fail(name != NULL, FALSE);134 g_return_val_if_fail(name != NULL, FALSE);
131135
132 if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) {
133 return FALSE;
134 }
135
136 g_debug("Loading Module: %s", name);136 g_debug("Loading Module: %s", name);
137137
138 /* Build the object for the module */138 /* Build the object for the module */
139 IndicatorObject * io = indicator_object_new_from_file(name);139 IndicatorObject *io;
140 if (g_str_has_suffix(name, G_MODULE_SUFFIX)) {
141 io = indicator_object_new_from_file(name);
142 }
143#if GTK_MAJOR_VERSION == 3
144 else if (g_str_has_suffix(name, ".indicator")) {
145 GError *error = NULL;
146
147 io = INDICATOR_OBJECT(indicator_ng_new(name, &error));
148 if (!io) {
149 g_warning ("could not load indicator from '%s': %s", name, error->message);
150 g_error_free (error);
151 return FALSE;
152 }
153 }
154#endif
155 else
156 return FALSE;
140157
141 /* Connect to it's signals */158 /* Connect to it's signals */
142 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menu);159 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menu);
143160
=== added file 'trim-lcov.py'
--- trim-lcov.py 1970-01-01 00:00:00 +0000
+++ trim-lcov.py 2013-02-14 21:19:21 +0000
@@ -0,0 +1,69 @@
1#!/usr/bin/python
2
3# Copyright 2013 Canonical Ltd.
4#
5# This library is free software; you can redistribute it and/or
6# modify it under the terms of the GNU General Public License
7# version 3.0 as published by the Free Software Foundation.
8#
9# This library is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License version 3.0 for more details.
13#
14# You should have received a copy of the GNU General Public
15# License along with this library. If not, see
16# <http://www.gnu.org/licenses/>.
17#
18# Author: Ryan Lortie <desrt@desrt.ca>
19
20
21# This script removes branch and/or line coverage data for lines that
22# contain a particular substring.
23#
24# In the interest of "fairness" it removes all branch or coverage data
25# when a match is found -- not just negative data. It is therefore
26# likely that running this script will actually reduce the total number
27# of lines and branches that are marked as covered (in absolute terms).
28#
29# This script intentionally avoids checking for errors. Any exceptions
30# will trigger make to fail.
31
32import sys
33
34line_suppress = ['g_assert_not_reached']
35branch_suppress = ['g_assert', 'g_return_if_fail', 'g_return_val_if_fail', 'G_DEFINE_TYPE']
36
37def check_suppress(suppressions, source, data):
38 line, _, rest = data.partition(',')
39 line = int(line) - 1
40
41 assert line < len(source)
42
43 for suppression in suppressions:
44 if suppression in source[line]:
45 return True
46
47 return False
48
49source = []
50for line in sys.stdin:
51 line = line[:-1]
52
53 keyword, _, rest = line.partition(':')
54
55 # Source file
56 if keyword == 'SF':
57 source = file(rest).readlines()
58
59 # Branch coverage data
60 elif keyword == 'BRDA':
61 if check_suppress(branch_suppress, source, rest):
62 continue
63
64 # Line coverage data
65 elif keyword == 'DA':
66 if check_suppress(line_suppress, source, rest):
67 continue
68
69 print line

Subscribers

People subscribed via source and target branches