gnome-shell-3.6.3.1/src/shell-app.c

Location Tool Test ID Function Issue
shell-app.c:163:7 gcc deprecated-declarations window_backed_app_get_icon 'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
shell-app.c:350:7 gcc deprecated-declarations shell_app_get_faded_icon 'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
shell-app.c:351:7 gcc deprecated-declarations shell_app_get_faded_icon 'clutter_texture_set_cogl_texture' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:80)
shell-app.c:355:7 gcc deprecated-declarations shell_app_get_faded_icon 'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 
   3 #include "config.h"
   4 
   5 #include <string.h>
   6 
   7 #include <glib/gi18n-lib.h>
   8 
   9 #include <meta/display.h>
  10 
  11 #include "shell-app-private.h"
  12 #include "shell-enum-types.h"
  13 #include "shell-global.h"
  14 #include "shell-util.h"
  15 #include "shell-app-system-private.h"
  16 #include "shell-window-tracker-private.h"
  17 #include "st.h"
  18 #include "gactionmuxer.h"
  19 
  20 typedef enum {
  21   MATCH_NONE,
  22   MATCH_SUBSTRING, /* Not prefix, substring */
  23   MATCH_PREFIX, /* Strict prefix */
  24 } ShellAppSearchMatch;
  25 
  26 /* This is mainly a memory usage optimization - the user is going to
  27  * be running far fewer of the applications at one time than they have
  28  * installed.  But it also just helps keep the code more logically
  29  * separated.
  30  */
  31 typedef struct {
  32   guint refcount;
  33 
  34   /* Signal connection to dirty window sort list on workspace changes */
  35   guint workspace_switch_id;
  36 
  37   GSList *windows;
  38 
  39   /* Whether or not we need to resort the windows; this is done on demand */
  40   gboolean window_sort_stale : 1;
  41 
  42   /* See GApplication documentation */
  43   GDBusMenuModel   *remote_menu;
  44   GActionMuxer     *muxer;
  45   char * unique_bus_name;
  46 } ShellAppRunningState;
  47 
  48 /**
  49  * SECTION:shell-app
  50  * @short_description: Object representing an application
  51  *
  52  * This object wraps a #GMenuTreeEntry, providing methods and signals
  53  * primarily useful for running applications.
  54  */
  55 struct _ShellApp
  56 {
  57   GObject parent;
  58 
  59   int started_on_workspace;
  60 
  61   ShellAppState state;
  62 
  63   GMenuTreeEntry *entry; /* If NULL, this app is backed by one or more
  64                           * MetaWindow.  For purposes of app title
  65                           * etc., we use the first window added,
  66                           * because it's most likely to be what we
  67                           * want (e.g. it will be of TYPE_NORMAL from
  68                           * the way shell-window-tracker.c works).
  69                           */
  70 
  71   ShellAppRunningState *running_state;
  72 
  73   char *window_id_string;
  74 
  75   char *casefolded_name;
  76   char *casefolded_generic_name;
  77   char *name_collation_key;
  78   char *casefolded_exec;
  79   char **casefolded_keywords;
  80 };
  81 
  82 enum {
  83   PROP_0,
  84   PROP_STATE,
  85   PROP_ID,
  86   PROP_DBUS_ID,
  87   PROP_ACTION_GROUP,
  88   PROP_MENU
  89 };
  90 
  91 enum {
  92   WINDOWS_CHANGED,
  93   LAST_SIGNAL
  94 };
  95 
  96 static guint shell_app_signals[LAST_SIGNAL] = { 0 };
  97 
  98 static void create_running_state (ShellApp *app);
  99 static void unref_running_state (ShellAppRunningState *state);
 100 
 101 G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
 102 
 103 static void
 104 shell_app_get_property (GObject    *gobject,
 105                         guint       prop_id,
 106                         GValue     *value,
 107                         GParamSpec *pspec)
 108 {
 109   ShellApp *app = SHELL_APP (gobject);
 110 
 111   switch (prop_id)
 112     {
 113     case PROP_STATE:
 114       g_value_set_enum (value, app->state);
 115       break;
 116     case PROP_ID:
 117       g_value_set_string (value, shell_app_get_id (app));
 118       break;
 119     case PROP_ACTION_GROUP:
 120       if (app->running_state)
 121         g_value_set_object (value, app->running_state->muxer);
 122       break;
 123     case PROP_MENU:
 124       if (app->running_state)
 125         g_value_set_object (value, app->running_state->remote_menu);
 126       break;
 127     default:
 128       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 129       break;
 130     }
 131 }
 132 
 133 const char *
 134 shell_app_get_id (ShellApp *app)
 135 {
 136   if (app->entry)
 137     return gmenu_tree_entry_get_desktop_file_id (app->entry);
 138   return app->window_id_string;
 139 }
 140 
 141 static MetaWindow *
 142 window_backed_app_get_window (ShellApp     *app)
 143 {
 144   g_assert (app->entry == NULL);
 145   g_assert (app->running_state);
 146   g_assert (app->running_state->windows);
 147   return app->running_state->windows->data;
 148 }
 149 
 150 static ClutterActor *
 151 window_backed_app_get_icon (ShellApp *app,
 152                             int       size)
 153 {
 154   MetaWindow *window;
 155   ClutterActor *actor;
 156 
 157   /* During a state transition from running to not-running for
 158    * window-backend apps, it's possible we get a request for the icon.
 159    * Avoid asserting here and just return an empty image.
 160    */
 161   if (app->running_state == NULL)
 162     {
 163       actor = clutter_texture_new ();
'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
(emitted by gcc)
164 g_object_set (actor, "opacity", 0, "width", (float) size, "height", (float) size, NULL); 165 return actor; 166 } 167 168 window = window_backed_app_get_window (app); 169 actor = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (), 170 G_OBJECT (window), 171 "icon"); 172 g_object_set (actor, "width", (float) size, "height", (float) size, NULL); 173 return actor; 174 } 175 176 /** 177 * shell_app_create_icon_texture: 178 * 179 * Look up the icon for this application, and create a #ClutterTexture 180 * for it at the given size. 181 * 182 * Return value: (transfer none): A floating #ClutterActor 183 */ 184 ClutterActor * 185 shell_app_create_icon_texture (ShellApp *app, 186 int size) 187 { 188 GIcon *icon; 189 ClutterActor *ret; 190 191 ret = NULL; 192 193 if (app->entry == NULL) 194 return window_backed_app_get_icon (app, size); 195 196 icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); 197 if (icon != NULL) 198 ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size); 199 200 if (ret == NULL) 201 { 202 icon = g_themed_icon_new ("application-x-executable"); 203 ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, size); 204 g_object_unref (icon); 205 } 206 207 return ret; 208 } 209 210 typedef struct { 211 ShellApp *app; 212 int size; 213 } CreateFadedIconData; 214 215 static CoglHandle 216 shell_app_create_faded_icon_cpu (StTextureCache *cache, 217 const char *key, 218 void *datap, 219 GError **error) 220 { 221 CreateFadedIconData *data = datap; 222 ShellApp *app; 223 GdkPixbuf *pixbuf; 224 int size; 225 CoglHandle texture; 226 gint width, height, rowstride; 227 guint8 n_channels; 228 gboolean have_alpha; 229 gint fade_start; 230 gint fade_range; 231 guint i, j; 232 guint pixbuf_byte_size; 233 guint8 *orig_pixels; 234 guint8 *pixels; 235 GIcon *icon; 236 GtkIconInfo *info; 237 238 app = data->app; 239 size = data->size; 240 241 info = NULL; 242 243 icon = g_app_info_get_icon (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); 244 if (icon != NULL) 245 { 246 info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), 247 icon, size, 248 GTK_ICON_LOOKUP_FORCE_SIZE); 249 } 250 251 if (info == NULL) 252 { 253 icon = g_themed_icon_new ("application-x-executable"); 254 info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), 255 icon, size, 256 GTK_ICON_LOOKUP_FORCE_SIZE); 257 g_object_unref (icon); 258 } 259 260 if (info == NULL) 261 return COGL_INVALID_HANDLE; 262 263 pixbuf = gtk_icon_info_load_icon (info, NULL); 264 gtk_icon_info_free (info); 265 266 if (pixbuf == NULL) 267 return COGL_INVALID_HANDLE; 268 269 width = gdk_pixbuf_get_width (pixbuf); 270 height = gdk_pixbuf_get_height (pixbuf); 271 rowstride = gdk_pixbuf_get_rowstride (pixbuf); 272 n_channels = gdk_pixbuf_get_n_channels (pixbuf); 273 orig_pixels = gdk_pixbuf_get_pixels (pixbuf); 274 have_alpha = gdk_pixbuf_get_has_alpha (pixbuf); 275 276 pixbuf_byte_size = (height - 1) * rowstride + 277 + width * ((n_channels * gdk_pixbuf_get_bits_per_sample (pixbuf) + 7) / 8); 278 279 pixels = g_malloc0 (rowstride * height); 280 memcpy (pixels, orig_pixels, pixbuf_byte_size); 281 282 fade_start = width / 2; 283 fade_range = width - fade_start; 284 for (i = fade_start; i < width; i++) 285 { 286 for (j = 0; j < height; j++) 287 { 288 guchar *pixel = &pixels[j * rowstride + i * n_channels]; 289 float fade = 1.0 - ((float) i - fade_start) / fade_range; 290 pixel[0] = 0.5 + pixel[0] * fade; 291 pixel[1] = 0.5 + pixel[1] * fade; 292 pixel[2] = 0.5 + pixel[2] * fade; 293 if (have_alpha) 294 pixel[3] = 0.5 + pixel[3] * fade; 295 } 296 } 297 298 texture = cogl_texture_new_from_data (width, 299 height, 300 COGL_TEXTURE_NONE, 301 have_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, 302 COGL_PIXEL_FORMAT_ANY, 303 rowstride, 304 pixels); 305 g_free (pixels); 306 g_object_unref (pixbuf); 307 308 return texture; 309 } 310 311 /** 312 * shell_app_get_faded_icon: 313 * @app: A #ShellApp 314 * @size: Size in pixels 315 * 316 * Return an actor with a horizontally faded look. 317 * 318 * Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon 319 */ 320 ClutterActor * 321 shell_app_get_faded_icon (ShellApp *app, int size) 322 { 323 CoglHandle texture; 324 ClutterActor *result; 325 char *cache_key; 326 CreateFadedIconData data; 327 328 /* Don't fade for window backed apps for now...easier to reuse the 329 * property tracking bits, and this helps us visually distinguish 330 * app-tracked from not. 331 */ 332 if (!app->entry) 333 return window_backed_app_get_icon (app, size); 334 335 /* Use icon: prefix so that we get evicted from the cache on 336 * icon theme changes. */ 337 cache_key = g_strdup_printf ("icon:%s,size=%d,faded", shell_app_get_id (app), size); 338 data.app = app; 339 data.size = size; 340 texture = st_texture_cache_load (st_texture_cache_get_default (), 341 cache_key, 342 ST_TEXTURE_CACHE_POLICY_FOREVER, 343 shell_app_create_faded_icon_cpu, 344 &data, 345 NULL); 346 g_free (cache_key); 347 348 if (texture != COGL_INVALID_HANDLE) 349 { 350 result = clutter_texture_new ();
'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
(emitted by gcc)
351 clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (result), texture);
'clutter_texture_set_cogl_texture' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:80)
(emitted by gcc)
352 } 353 else 354 { 355 result = clutter_texture_new ();
'clutter_texture_new' is deprecated (declared at /usr/include/clutter-1.0/clutter/deprecated/clutter-texture.h:36): Use 'clutter_image_new' instead
(emitted by gcc)
356 g_object_set (result, "opacity", 0, "width", (float) size, "height", (float) size, NULL); 357 358 } 359 return result; 360 } 361 362 const char * 363 shell_app_get_name (ShellApp *app) 364 { 365 if (app->entry) 366 return g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); 367 else 368 { 369 MetaWindow *window = window_backed_app_get_window (app); 370 const char *name; 371 372 name = meta_window_get_wm_class (window); 373 if (!name) 374 name = C_("program", "Unknown"); 375 return name; 376 } 377 } 378 379 const char * 380 shell_app_get_description (ShellApp *app) 381 { 382 if (app->entry) 383 return g_app_info_get_description (G_APP_INFO (gmenu_tree_entry_get_app_info (app->entry))); 384 else 385 return NULL; 386 } 387 388 /** 389 * shell_app_is_window_backed: 390 * 391 * A window backed application is one which represents just an open 392 * window, i.e. there's no .desktop file assocation, so we don't know 393 * how to launch it again. 394 */ 395 gboolean 396 shell_app_is_window_backed (ShellApp *app) 397 { 398 return app->entry == NULL; 399 } 400 401 typedef struct { 402 MetaWorkspace *workspace; 403 GSList **transients; 404 } CollectTransientsData; 405 406 static gboolean 407 collect_transients_on_workspace (MetaWindow *window, 408 gpointer datap) 409 { 410 CollectTransientsData *data = datap; 411 412 if (data->workspace && meta_window_get_workspace (window) != data->workspace) 413 return TRUE; 414 415 *data->transients = g_slist_prepend (*data->transients, window); 416 return TRUE; 417 } 418 419 /* The basic idea here is that when we're targeting a window, 420 * if it has transients we want to pick the most recent one 421 * the user interacted with. 422 * This function makes raising GEdit with the file chooser 423 * open work correctly. 424 */ 425 static MetaWindow * 426 find_most_recent_transient_on_same_workspace (MetaDisplay *display, 427 MetaWindow *reference) 428 { 429 GSList *transients, *transients_sorted, *iter; 430 MetaWindow *result; 431 CollectTransientsData data; 432 433 transients = NULL; 434 data.workspace = meta_window_get_workspace (reference); 435 data.transients = &transients; 436 437 meta_window_foreach_transient (reference, collect_transients_on_workspace, &data); 438 439 transients_sorted = meta_display_sort_windows_by_stacking (display, transients); 440 /* Reverse this so we're top-to-bottom (yes, we should probably change the order 441 * returned from the sort_windows_by_stacking function) 442 */ 443 transients_sorted = g_slist_reverse (transients_sorted); 444 g_slist_free (transients); 445 transients = NULL; 446 447 result = NULL; 448 for (iter = transients_sorted; iter; iter = iter->next) 449 { 450 MetaWindow *window = iter->data; 451 MetaWindowType wintype = meta_window_get_window_type (window); 452 453 /* Don't want to focus UTILITY types, like the Gimp toolbars */ 454 if (wintype == META_WINDOW_NORMAL || 455 wintype == META_WINDOW_DIALOG) 456 { 457 result = window; 458 break; 459 } 460 } 461 g_slist_free (transients_sorted); 462 return result; 463 } 464 465 /** 466 * shell_app_activate_window: 467 * @app: a #ShellApp 468 * @window: (allow-none): Window to be focused 469 * @timestamp: Event timestamp 470 * 471 * Bring all windows for the given app to the foreground, 472 * but ensure that @window is on top. If @window is %NULL, 473 * the window with the most recent user time for the app 474 * will be used. 475 * 476 * This function has no effect if @app is not currently running. 477 */ 478 void 479 shell_app_activate_window (ShellApp *app, 480 MetaWindow *window, 481 guint32 timestamp) 482 { 483 GSList *windows; 484 485 if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING) 486 return; 487 488 windows = shell_app_get_windows (app); 489 if (window == NULL && windows) 490 window = windows->data; 491 492 if (!g_slist_find (windows, window)) 493 return; 494 else 495 { 496 GSList *windows_reversed, *iter; 497 ShellGlobal *global = shell_global_get (); 498 MetaScreen *screen = shell_global_get_screen (global); 499 MetaDisplay *display = meta_screen_get_display (screen); 500 MetaWorkspace *active = meta_screen_get_active_workspace (screen); 501 MetaWorkspace *workspace = meta_window_get_workspace (window); 502 guint32 last_user_timestamp = meta_display_get_last_user_time (display); 503 MetaWindow *most_recent_transient; 504 505 if (meta_display_xserver_time_is_before (display, timestamp, last_user_timestamp)) 506 { 507 meta_window_set_demands_attention (window); 508 return; 509 } 510 511 /* Now raise all the other windows for the app that are on 512 * the same workspace, in reverse order to preserve the stacking. 513 */ 514 windows_reversed = g_slist_copy (windows); 515 windows_reversed = g_slist_reverse (windows_reversed); 516 for (iter = windows_reversed; iter; iter = iter->next) 517 { 518 MetaWindow *other_window = iter->data; 519 520 if (other_window != window) 521 meta_window_raise (other_window); 522 } 523 g_slist_free (windows_reversed); 524 525 /* If we have a transient that the user's interacted with more recently than 526 * the window, pick that. 527 */ 528 most_recent_transient = find_most_recent_transient_on_same_workspace (display, window); 529 if (most_recent_transient 530 && meta_display_xserver_time_is_before (display, 531 meta_window_get_user_time (window), 532 meta_window_get_user_time (most_recent_transient))) 533 window = most_recent_transient; 534 535 536 if (active != workspace) 537 meta_workspace_activate_with_focus (workspace, window, timestamp); 538 else 539 meta_window_activate (window, timestamp); 540 } 541 } 542 543 544 void 545 shell_app_update_window_actions (ShellApp *app, MetaWindow *window) 546 { 547 const char *object_path; 548 549 object_path = meta_window_get_gtk_window_object_path (window); 550 if (object_path != NULL) 551 { 552 GActionGroup *actions; 553 554 actions = g_object_get_data (G_OBJECT (window), "actions"); 555 if (actions == NULL) 556 { 557 actions = G_ACTION_GROUP (g_dbus_action_group_get (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL), 558 meta_window_get_gtk_unique_bus_name (window), 559 object_path)); 560 g_object_set_data_full (G_OBJECT (window), "actions", actions, g_object_unref); 561 } 562 563 if (!app->running_state->muxer) 564 app->running_state->muxer = g_action_muxer_new (); 565 566 g_action_muxer_insert (app->running_state->muxer, "win", actions); 567 g_object_notify (G_OBJECT (app), "action-group"); 568 } 569 } 570 571 /** 572 * shell_app_activate: 573 * @app: a #ShellApp 574 * 575 * Like shell_app_activate_full(), but using the default workspace and 576 * event timestamp. 577 */ 578 void 579 shell_app_activate (ShellApp *app) 580 { 581 return shell_app_activate_full (app, -1, 0); 582 } 583 584 /** 585 * shell_app_activate_full: 586 * @app: a #ShellApp 587 * @workspace: launch on this workspace, or -1 for default. Ignored if 588 * activating an existing window 589 * @timestamp: Event timestamp 590 * 591 * Perform an appropriate default action for operating on this application, 592 * dependent on its current state. For example, if the application is not 593 * currently running, launch it. If it is running, activate the most 594 * recently used NORMAL window (or if that window has a transient, the most 595 * recently used transient for that window). 596 */ 597 void 598 shell_app_activate_full (ShellApp *app, 599 int workspace, 600 guint32 timestamp) 601 { 602 ShellGlobal *global; 603 604 global = shell_global_get (); 605 606 if (timestamp == 0) 607 timestamp = shell_global_get_current_time (global); 608 609 switch (app->state) 610 { 611 case SHELL_APP_STATE_STOPPED: 612 { 613 GError *error = NULL; 614 if (!shell_app_launch (app, 615 timestamp, 616 NULL, 617 workspace, 618 NULL, 619 &error)) 620 { 621 char *msg; 622 msg = g_strdup_printf (_("Failed to launch '%s'"), shell_app_get_name (app)); 623 shell_global_notify_error (global, 624 msg, 625 error->message); 626 g_free (msg); 627 g_clear_error (&error); 628 } 629 } 630 break; 631 case SHELL_APP_STATE_STARTING: 632 break; 633 case SHELL_APP_STATE_RUNNING: 634 shell_app_activate_window (app, NULL, timestamp); 635 break; 636 } 637 } 638 639 /** 640 * shell_app_open_new_window: 641 * @app: a #ShellApp 642 * @workspace: open on this workspace, or -1 for default 643 * 644 * Request that the application create a new window. 645 */ 646 void 647 shell_app_open_new_window (ShellApp *app, 648 int workspace) 649 { 650 g_return_if_fail (app->entry != NULL); 651 652 /* Here we just always launch the application again, even if we know 653 * it was already running. For most applications this 654 * should have the effect of creating a new window, whether that's 655 * a second process (in the case of Calculator) or IPC to existing 656 * instance (Firefox). There are a few less-sensical cases such 657 * as say Pidgin. Ideally, we have the application express to us 658 * that it supports an explicit new-window action. 659 */ 660 shell_app_launch (app, 661 0, 662 NULL, 663 workspace, 664 NULL, 665 NULL); 666 } 667 668 /** 669 * shell_app_get_state: 670 * @app: a #ShellApp 671 * 672 * Returns: State of the application 673 */ 674 ShellAppState 675 shell_app_get_state (ShellApp *app) 676 { 677 return app->state; 678 } 679 680 typedef struct { 681 ShellApp *app; 682 MetaWorkspace *active_workspace; 683 } CompareWindowsData; 684 685 static int 686 shell_app_compare_windows (gconstpointer a, 687 gconstpointer b, 688 gpointer datap) 689 { 690 MetaWindow *win_a = (gpointer)a; 691 MetaWindow *win_b = (gpointer)b; 692 CompareWindowsData *data = datap; 693 gboolean ws_a, ws_b; 694 gboolean vis_a, vis_b; 695 696 ws_a = meta_window_get_workspace (win_a) == data->active_workspace; 697 ws_b = meta_window_get_workspace (win_b) == data->active_workspace; 698 699 if (ws_a && !ws_b) 700 return -1; 701 else if (!ws_a && ws_b) 702 return 1; 703 704 vis_a = meta_window_showing_on_its_workspace (win_a); 705 vis_b = meta_window_showing_on_its_workspace (win_b); 706 707 if (vis_a && !vis_b) 708 return -1; 709 else if (!vis_a && vis_b) 710 return 1; 711 712 return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a); 713 } 714 715 /** 716 * shell_app_get_windows: 717 * @app: 718 * 719 * Get the toplevel, interesting windows which are associated with this 720 * application. The returned list will be sorted first by whether 721 * they're on the active workspace, then by whether they're visible, 722 * and finally by the time the user last interacted with them. 723 * 724 * Returns: (transfer none) (element-type MetaWindow): List of windows 725 */ 726 GSList * 727 shell_app_get_windows (ShellApp *app) 728 { 729 if (app->running_state == NULL) 730 return NULL; 731 732 if (app->running_state->window_sort_stale) 733 { 734 CompareWindowsData data; 735 data.app = app; 736 data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ())); 737 app->running_state->windows = g_slist_sort_with_data (app->running_state->windows, shell_app_compare_windows, &data); 738 app->running_state->window_sort_stale = FALSE; 739 } 740 741 return app->running_state->windows; 742 } 743 744 guint 745 shell_app_get_n_windows (ShellApp *app) 746 { 747 if (app->running_state == NULL) 748 return 0; 749 return g_slist_length (app->running_state->windows); 750 } 751 752 static gboolean 753 shell_app_has_visible_windows (ShellApp *app) 754 { 755 GSList *iter; 756 757 if (app->running_state == NULL) 758 return FALSE; 759 760 for (iter = app->running_state->windows; iter; iter = iter->next) 761 { 762 MetaWindow *window = iter->data; 763 764 if (meta_window_showing_on_its_workspace (window)) 765 return TRUE; 766 } 767 768 return FALSE; 769 } 770 771 gboolean 772 shell_app_is_on_workspace (ShellApp *app, 773 MetaWorkspace *workspace) 774 { 775 GSList *iter; 776 777 if (shell_app_get_state (app) == SHELL_APP_STATE_STARTING) 778 { 779 if (app->started_on_workspace == -1 || 780 meta_workspace_index (workspace) == app->started_on_workspace) 781 return TRUE; 782 else 783 return FALSE; 784 } 785 786 if (app->running_state == NULL) 787 return FALSE; 788 789 for (iter = app->running_state->windows; iter; iter = iter->next) 790 { 791 if (meta_window_get_workspace (iter->data) == workspace) 792 return TRUE; 793 } 794 795 return FALSE; 796 } 797 798 static int 799 shell_app_get_last_user_time (ShellApp *app) 800 { 801 GSList *iter; 802 int last_user_time; 803 804 last_user_time = 0; 805 806 if (app->running_state != NULL) 807 { 808 for (iter = app->running_state->windows; iter; iter = iter->next) 809 last_user_time = MAX (last_user_time, meta_window_get_user_time (iter->data)); 810 } 811 812 return last_user_time; 813 } 814 815 /** 816 * shell_app_compare: 817 * @app: 818 * @other: A #ShellApp 819 * 820 * Compare one #ShellApp instance to another, in the following way: 821 * - Running applications sort before not-running applications. 822 * - If one of them has visible windows and the other does not, the one 823 * with visible windows is first. 824 * - Finally, the application which the user interacted with most recently 825 * compares earlier. 826 */ 827 int 828 shell_app_compare (ShellApp *app, 829 ShellApp *other) 830 { 831 gboolean vis_app, vis_other; 832 833 if (app->state != other->state) 834 { 835 if (app->state == SHELL_APP_STATE_RUNNING) 836 return -1; 837 return 1; 838 } 839 840 vis_app = shell_app_has_visible_windows (app); 841 vis_other = shell_app_has_visible_windows (other); 842 843 if (vis_app && !vis_other) 844 return -1; 845 else if (!vis_app && vis_other) 846 return 1; 847 848 if (app->state == SHELL_APP_STATE_RUNNING) 849 { 850 if (app->running_state->windows && !other->running_state->windows) 851 return -1; 852 else if (!app->running_state->windows && other->running_state->windows) 853 return 1; 854 855 return shell_app_get_last_user_time (other) - shell_app_get_last_user_time (app); 856 } 857 858 return 0; 859 } 860 861 ShellApp * 862 _shell_app_new_for_window (MetaWindow *window) 863 { 864 ShellApp *app; 865 866 app = g_object_new (SHELL_TYPE_APP, NULL); 867 868 app->window_id_string = g_strdup_printf ("window:%d", meta_window_get_stable_sequence (window)); 869 870 _shell_app_add_window (app, window); 871 872 return app; 873 } 874 875 ShellApp * 876 _shell_app_new (GMenuTreeEntry *info) 877 { 878 ShellApp *app; 879 880 app = g_object_new (SHELL_TYPE_APP, NULL); 881 882 _shell_app_set_entry (app, info); 883 884 return app; 885 } 886 887 void 888 _shell_app_set_entry (ShellApp *app, 889 GMenuTreeEntry *entry) 890 { 891 if (app->entry != NULL) 892 gmenu_tree_item_unref (app->entry); 893 app->entry = gmenu_tree_item_ref (entry); 894 895 if (app->name_collation_key != NULL) 896 g_free (app->name_collation_key); 897 app->name_collation_key = g_utf8_collate_key (shell_app_get_name (app), -1); 898 } 899 900 static void 901 shell_app_state_transition (ShellApp *app, 902 ShellAppState state) 903 { 904 if (app->state == state) 905 return; 906 g_return_if_fail (!(app->state == SHELL_APP_STATE_RUNNING && 907 state == SHELL_APP_STATE_STARTING)); 908 app->state = state; 909 910 if (app->state == SHELL_APP_STATE_STOPPED && app->running_state) 911 { 912 unref_running_state (app->running_state); 913 app->running_state = NULL; 914 } 915 916 _shell_app_system_notify_app_state_changed (shell_app_system_get_default (), app); 917 918 g_object_notify (G_OBJECT (app), "state"); 919 } 920 921 static void 922 shell_app_on_unmanaged (MetaWindow *window, 923 ShellApp *app) 924 { 925 _shell_app_remove_window (app, window); 926 } 927 928 static void 929 shell_app_on_user_time_changed (MetaWindow *window, 930 GParamSpec *pspec, 931 ShellApp *app) 932 { 933 g_assert (app->running_state != NULL); 934 935 /* Ideally we don't want to emit windows-changed if the sort order 936 * isn't actually changing. This check catches most of those. 937 */ 938 if (window != app->running_state->windows->data) 939 { 940 app->running_state->window_sort_stale = TRUE; 941 g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0); 942 } 943 } 944 945 static void 946 shell_app_on_ws_switch (MetaScreen *screen, 947 int from, 948 int to, 949 MetaMotionDirection direction, 950 gpointer data) 951 { 952 ShellApp *app = SHELL_APP (data); 953 954 g_assert (app->running_state != NULL); 955 956 app->running_state->window_sort_stale = TRUE; 957 958 g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0); 959 } 960 961 void 962 _shell_app_add_window (ShellApp *app, 963 MetaWindow *window) 964 { 965 if (app->running_state && g_slist_find (app->running_state->windows, window)) 966 return; 967 968 g_object_freeze_notify (G_OBJECT (app)); 969 970 if (!app->running_state) 971 create_running_state (app); 972 973 app->running_state->window_sort_stale = TRUE; 974 app->running_state->windows = g_slist_prepend (app->running_state->windows, g_object_ref (window)); 975 g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app); 976 g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app); 977 978 shell_app_update_app_menu (app, window); 979 980 if (app->state != SHELL_APP_STATE_STARTING) 981 shell_app_state_transition (app, SHELL_APP_STATE_RUNNING); 982 983 g_object_thaw_notify (G_OBJECT (app)); 984 985 g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0); 986 } 987 988 void 989 _shell_app_remove_window (ShellApp *app, 990 MetaWindow *window) 991 { 992 g_assert (app->running_state != NULL); 993 994 if (!g_slist_find (app->running_state->windows, window)) 995 return; 996 997 g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app); 998 g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app); 999 g_object_unref (window); 1000 app->running_state->windows = g_slist_remove (app->running_state->windows, window); 1001 1002 if (app->running_state->windows == NULL) 1003 shell_app_state_transition (app, SHELL_APP_STATE_STOPPED); 1004 1005 g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0); 1006 } 1007 1008 /** 1009 * shell_app_get_pids: 1010 * @app: a #ShellApp 1011 * 1012 * Returns: (transfer container) (element-type int): An unordered list of process identifers associated with this application. 1013 */ 1014 GSList * 1015 shell_app_get_pids (ShellApp *app) 1016 { 1017 GSList *result; 1018 GSList *iter; 1019 1020 result = NULL; 1021 for (iter = shell_app_get_windows (app); iter; iter = iter->next) 1022 { 1023 MetaWindow *window = iter->data; 1024 int pid = meta_window_get_pid (window); 1025 /* Note in the (by far) common case, app will only have one pid, so 1026 * we'll hit the first element, so don't worry about O(N^2) here. 1027 */ 1028 if (!g_slist_find (result, GINT_TO_POINTER (pid))) 1029 result = g_slist_prepend (result, GINT_TO_POINTER (pid)); 1030 } 1031 return result; 1032 } 1033 1034 void 1035 _shell_app_handle_startup_sequence (ShellApp *app, 1036 SnStartupSequence *sequence) 1037 { 1038 gboolean starting = !sn_startup_sequence_get_completed (sequence); 1039 1040 /* The Shell design calls for on application launch, the app title 1041 * appears at top, and no X window is focused. So when we get 1042 * a startup-notification for this app, transition it to STARTING 1043 * if it's currently stopped, set it as our application focus, 1044 * but focus the no_focus window. 1045 */ 1046 if (starting && shell_app_get_state (app) == SHELL_APP_STATE_STOPPED) 1047 { 1048 MetaScreen *screen = shell_global_get_screen (shell_global_get ()); 1049 MetaDisplay *display = meta_screen_get_display (screen); 1050 1051 shell_app_state_transition (app, SHELL_APP_STATE_STARTING); 1052 meta_display_focus_the_no_focus_window (display, screen, 1053 sn_startup_sequence_get_timestamp (sequence)); 1054 app->started_on_workspace = sn_startup_sequence_get_workspace (sequence); 1055 } 1056 1057 if (!starting) 1058 { 1059 if (app->running_state && app->running_state->windows) 1060 shell_app_state_transition (app, SHELL_APP_STATE_RUNNING); 1061 else /* application have > 1 .desktop file */ 1062 shell_app_state_transition (app, SHELL_APP_STATE_STOPPED); 1063 } 1064 } 1065 1066 /** 1067 * shell_app_request_quit: 1068 * @app: A #ShellApp 1069 * 1070 * Initiate an asynchronous request to quit this application. 1071 * The application may interact with the user, and the user 1072 * might cancel the quit request from the application UI. 1073 * 1074 * This operation may not be supported for all applications. 1075 * 1076 * Returns: %TRUE if a quit request is supported for this application 1077 */ 1078 gboolean 1079 shell_app_request_quit (ShellApp *app) 1080 { 1081 GSList *iter; 1082 1083 if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING) 1084 return FALSE; 1085 1086 /* TODO - check for an XSMP connection; we could probably use that */ 1087 1088 for (iter = app->running_state->windows; iter; iter = iter->next) 1089 { 1090 MetaWindow *win = iter->data; 1091 1092 if (!shell_window_tracker_is_window_interesting (win)) 1093 continue; 1094 1095 meta_window_delete (win, shell_global_get_current_time (shell_global_get ())); 1096 } 1097 return TRUE; 1098 } 1099 1100 static void 1101 _gather_pid_callback (GDesktopAppInfo *gapp, 1102 GPid pid, 1103 gpointer data) 1104 { 1105 ShellApp *app; 1106 ShellWindowTracker *tracker; 1107 1108 g_return_if_fail (data != NULL); 1109 1110 app = SHELL_APP (data); 1111 tracker = shell_window_tracker_get_default (); 1112 1113 _shell_window_tracker_add_child_process_app (tracker, 1114 pid, 1115 app); 1116 } 1117 1118 /** 1119 * shell_app_launch: 1120 * @timestamp: Event timestamp, or 0 for current event timestamp 1121 * @uris: (element-type utf8): List of uris to pass to application 1122 * @workspace: Start on this workspace, or -1 for default 1123 * @startup_id: (out): Returned startup notification ID, or %NULL if none 1124 * @error: A #GError 1125 */ 1126 gboolean 1127 shell_app_launch (ShellApp *app, 1128 guint timestamp, 1129 GList *uris, 1130 int workspace, 1131 char **startup_id, 1132 GError **error) 1133 { 1134 GDesktopAppInfo *gapp; 1135 GdkAppLaunchContext *context; 1136 gboolean ret; 1137 ShellGlobal *global; 1138 MetaScreen *screen; 1139 GdkDisplay *gdisplay; 1140 1141 if (startup_id) 1142 *startup_id = NULL; 1143 1144 if (app->entry == NULL) 1145 { 1146 MetaWindow *window = window_backed_app_get_window (app); 1147 /* We can't pass URIs into a window; shouldn't hit this 1148 * code path. If we do, fix the caller to disallow it. 1149 */ 1150 g_return_val_if_fail (uris == NULL, TRUE); 1151 1152 meta_window_activate (window, timestamp); 1153 return TRUE; 1154 } 1155 1156 global = shell_global_get (); 1157 screen = shell_global_get_screen (global); 1158 gdisplay = gdk_screen_get_display (shell_global_get_gdk_screen (global)); 1159 1160 if (timestamp == 0) 1161 timestamp = shell_global_get_current_time (global); 1162 1163 if (workspace < 0) 1164 workspace = meta_screen_get_active_workspace_index (screen); 1165 1166 context = gdk_display_get_app_launch_context (gdisplay); 1167 gdk_app_launch_context_set_timestamp (context, timestamp); 1168 gdk_app_launch_context_set_desktop (context, workspace); 1169 1170 gapp = gmenu_tree_entry_get_app_info (app->entry); 1171 ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris, 1172 G_APP_LAUNCH_CONTEXT (context), 1173 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, 1174 NULL, NULL, 1175 _gather_pid_callback, app, 1176 error); 1177 g_object_unref (context); 1178 1179 return ret; 1180 } 1181 1182 /** 1183 * shell_app_get_app_info: 1184 * @app: a #ShellApp 1185 * 1186 * Returns: (transfer none): The #GDesktopAppInfo for this app, or %NULL if backed by a window 1187 */ 1188 GDesktopAppInfo * 1189 shell_app_get_app_info (ShellApp *app) 1190 { 1191 if (app->entry) 1192 return gmenu_tree_entry_get_app_info (app->entry); 1193 return NULL; 1194 } 1195 1196 /** 1197 * shell_app_get_tree_entry: 1198 * @app: a #ShellApp 1199 * 1200 * Returns: (transfer none): The #GMenuTreeEntry for this app, or %NULL if backed by a window 1201 */ 1202 GMenuTreeEntry * 1203 shell_app_get_tree_entry (ShellApp *app) 1204 { 1205 return app->entry; 1206 } 1207 1208 static void 1209 create_running_state (ShellApp *app) 1210 { 1211 MetaScreen *screen; 1212 1213 g_assert (app->running_state == NULL); 1214 1215 screen = shell_global_get_screen (shell_global_get ()); 1216 app->running_state = g_slice_new0 (ShellAppRunningState); 1217 app->running_state->refcount = 1; 1218 app->running_state->workspace_switch_id = 1219 g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app); 1220 1221 app->running_state->muxer = g_action_muxer_new (); 1222 } 1223 1224 void 1225 shell_app_update_app_menu (ShellApp *app, 1226 MetaWindow *window) 1227 { 1228 const gchar *unique_bus_name; 1229 1230 /* We assume that 'gtk-application-object-path' and 1231 * 'gtk-app-menu-object-path' are the same for all windows which 1232 * have it set. 1233 * 1234 * It could be possible, however, that the first window we see 1235 * belonging to the app didn't have them set. For this reason, we 1236 * take the values from the first window that has them set and ignore 1237 * all the rest (until the app is stopped and restarted). 1238 */ 1239 1240 unique_bus_name = meta_window_get_gtk_unique_bus_name (window); 1241 1242 if (app->running_state->remote_menu == NULL || 1243 g_strcmp0 (app->running_state->unique_bus_name, unique_bus_name) != 0) 1244 { 1245 const gchar *application_object_path; 1246 const gchar *app_menu_object_path; 1247 GDBusConnection *session; 1248 GDBusActionGroup *actions; 1249 1250 application_object_path = meta_window_get_gtk_application_object_path (window); 1251 app_menu_object_path = meta_window_get_gtk_app_menu_object_path (window); 1252 1253 if (application_object_path == NULL || app_menu_object_path == NULL || unique_bus_name == NULL) 1254 return; 1255 1256 session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); 1257 g_assert (session != NULL); 1258 g_clear_pointer (&app->running_state->unique_bus_name, g_free); 1259 app->running_state->unique_bus_name = g_strdup (unique_bus_name); 1260 g_clear_object (&app->running_state->remote_menu); 1261 app->running_state->remote_menu = g_dbus_menu_model_get (session, unique_bus_name, app_menu_object_path); 1262 actions = g_dbus_action_group_get (session, unique_bus_name, application_object_path); 1263 g_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions)); 1264 g_object_unref (actions); 1265 g_object_unref (session); 1266 } 1267 } 1268 1269 static void 1270 unref_running_state (ShellAppRunningState *state) 1271 { 1272 MetaScreen *screen; 1273 1274 g_assert (state->refcount > 0); 1275 1276 state->refcount--; 1277 if (state->refcount > 0) 1278 return; 1279 1280 screen = shell_global_get_screen (shell_global_get ()); 1281 g_signal_handler_disconnect (screen, state->workspace_switch_id); 1282 1283 g_clear_object (&state->remote_menu); 1284 g_clear_object (&state->muxer); 1285 g_clear_pointer (&state->unique_bus_name, g_free); 1286 g_clear_pointer (&state->remote_menu, g_free); 1287 1288 g_slice_free (ShellAppRunningState, state); 1289 } 1290 1291 static char * 1292 trim_exec_line (const char *str) 1293 { 1294 const char *start, *end, *pos; 1295 1296 if (str == NULL) 1297 return NULL; 1298 1299 end = strchr (str, ' '); 1300 if (end == NULL) 1301 end = str + strlen (str); 1302 1303 start = str; 1304 while ((pos = strchr (start, '/')) && pos < end) 1305 start = ++pos; 1306 1307 return g_strndup (start, end - start); 1308 } 1309 1310 static void 1311 shell_app_init_search_data (ShellApp *app) 1312 { 1313 const char *name; 1314 const char *generic_name; 1315 const char *exec; 1316 const char * const *keywords; 1317 char *normalized_exec; 1318 GDesktopAppInfo *appinfo; 1319 1320 appinfo = gmenu_tree_entry_get_app_info (app->entry); 1321 name = g_app_info_get_name (G_APP_INFO (appinfo)); 1322 app->casefolded_name = shell_util_normalize_and_casefold (name); 1323 1324 generic_name = g_desktop_app_info_get_generic_name (appinfo); 1325 if (generic_name) 1326 app->casefolded_generic_name = shell_util_normalize_and_casefold (generic_name); 1327 else 1328 app->casefolded_generic_name = NULL; 1329 1330 exec = g_app_info_get_executable (G_APP_INFO (appinfo)); 1331 normalized_exec = shell_util_normalize_and_casefold (exec); 1332 app->casefolded_exec = trim_exec_line (normalized_exec); 1333 g_free (normalized_exec); 1334 1335 keywords = g_desktop_app_info_get_keywords (appinfo); 1336 1337 if (keywords) 1338 { 1339 int i; 1340 1341 app->casefolded_keywords = g_new0 (char*, g_strv_length ((char **)keywords) + 1); 1342 1343 i = 0; 1344 while (keywords[i]) 1345 { 1346 app->casefolded_keywords[i] = shell_util_normalize_and_casefold (keywords[i]); 1347 ++i; 1348 } 1349 app->casefolded_keywords[i] = NULL; 1350 } 1351 else 1352 app->casefolded_keywords = NULL; 1353 } 1354 1355 /** 1356 * shell_app_compare_by_name: 1357 * @app: One app 1358 * @other: The other app 1359 * 1360 * Order two applications by name. 1361 * 1362 * Returns: -1, 0, or 1; suitable for use as a comparison function 1363 * for e.g. g_slist_sort() 1364 */ 1365 int 1366 shell_app_compare_by_name (ShellApp *app, ShellApp *other) 1367 { 1368 return strcmp (app->name_collation_key, other->name_collation_key); 1369 } 1370 1371 static ShellAppSearchMatch 1372 _shell_app_match_search_terms (ShellApp *app, 1373 GSList *terms) 1374 { 1375 GSList *iter; 1376 ShellAppSearchMatch match; 1377 1378 if (G_UNLIKELY (!app->casefolded_name)) 1379 shell_app_init_search_data (app); 1380 1381 match = MATCH_NONE; 1382 for (iter = terms; iter; iter = iter->next) 1383 { 1384 ShellAppSearchMatch current_match; 1385 const char *term = iter->data; 1386 const char *p; 1387 1388 current_match = MATCH_NONE; 1389 1390 p = strstr (app->casefolded_name, term); 1391 if (p != NULL) 1392 { 1393 if (p == app->casefolded_name || *(p - 1) == ' ') 1394 current_match = MATCH_PREFIX; 1395 else 1396 current_match = MATCH_SUBSTRING; 1397 } 1398 1399 if (app->casefolded_generic_name) 1400 { 1401 p = strstr (app->casefolded_generic_name, term); 1402 if (p != NULL) 1403 { 1404 if (p == app->casefolded_generic_name || *(p - 1) == ' ') 1405 current_match = MATCH_PREFIX; 1406 else if (current_match < MATCH_PREFIX) 1407 current_match = MATCH_SUBSTRING; 1408 } 1409 } 1410 1411 if (app->casefolded_exec) 1412 { 1413 p = strstr (app->casefolded_exec, term); 1414 if (p != NULL) 1415 { 1416 if (p == app->casefolded_exec || *(p - 1) == '-') 1417 current_match = MATCH_PREFIX; 1418 else if (current_match < MATCH_PREFIX) 1419 current_match = MATCH_SUBSTRING; 1420 } 1421 } 1422 1423 if (app->casefolded_keywords) 1424 { 1425 int i = 0; 1426 while (app->casefolded_keywords[i] && current_match < MATCH_PREFIX) 1427 { 1428 p = strstr (app->casefolded_keywords[i], term); 1429 if (p != NULL) 1430 { 1431 if (p == app->casefolded_keywords[i]) 1432 current_match = MATCH_PREFIX; 1433 else 1434 current_match = MATCH_SUBSTRING; 1435 } 1436 ++i; 1437 } 1438 } 1439 1440 if (current_match == MATCH_NONE) 1441 return current_match; 1442 1443 if (current_match > match) 1444 match = current_match; 1445 } 1446 return match; 1447 } 1448 1449 void 1450 _shell_app_do_match (ShellApp *app, 1451 GSList *terms, 1452 GSList **prefix_results, 1453 GSList **substring_results) 1454 { 1455 ShellAppSearchMatch match; 1456 GAppInfo *appinfo; 1457 1458 g_assert (app != NULL); 1459 1460 /* Skip window-backed apps */ 1461 appinfo = (GAppInfo*)shell_app_get_app_info (app); 1462 if (appinfo == NULL) 1463 return; 1464 /* Skip not-visible apps */ 1465 if (!g_app_info_should_show (appinfo)) 1466 return; 1467 1468 match = _shell_app_match_search_terms (app, terms); 1469 switch (match) 1470 { 1471 case MATCH_NONE: 1472 break; 1473 case MATCH_PREFIX: 1474 *prefix_results = g_slist_prepend (*prefix_results, app); 1475 break; 1476 case MATCH_SUBSTRING: 1477 *substring_results = g_slist_prepend (*substring_results, app); 1478 break; 1479 } 1480 } 1481 1482 1483 static void 1484 shell_app_init (ShellApp *self) 1485 { 1486 self->state = SHELL_APP_STATE_STOPPED; 1487 } 1488 1489 static void 1490 shell_app_dispose (GObject *object) 1491 { 1492 ShellApp *app = SHELL_APP (object); 1493 1494 if (app->entry) 1495 { 1496 gmenu_tree_item_unref (app->entry); 1497 app->entry = NULL; 1498 } 1499 1500 if (app->running_state) 1501 { 1502 while (app->running_state->windows) 1503 _shell_app_remove_window (app, app->running_state->windows->data); 1504 } 1505 /* We should have been transitioned when we removed all of our windows */ 1506 g_assert (app->state == SHELL_APP_STATE_STOPPED); 1507 g_assert (app->running_state == NULL); 1508 1509 G_OBJECT_CLASS(shell_app_parent_class)->dispose (object); 1510 } 1511 1512 static void 1513 shell_app_finalize (GObject *object) 1514 { 1515 ShellApp *app = SHELL_APP (object); 1516 1517 g_free (app->window_id_string); 1518 1519 g_free (app->casefolded_name); 1520 g_free (app->casefolded_generic_name); 1521 g_free (app->name_collation_key); 1522 g_free (app->casefolded_exec); 1523 g_strfreev (app->casefolded_keywords); 1524 1525 G_OBJECT_CLASS(shell_app_parent_class)->finalize (object); 1526 } 1527 1528 static void 1529 shell_app_class_init(ShellAppClass *klass) 1530 { 1531 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 1532 1533 gobject_class->get_property = shell_app_get_property; 1534 gobject_class->dispose = shell_app_dispose; 1535 gobject_class->finalize = shell_app_finalize; 1536 1537 shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed", 1538 SHELL_TYPE_APP, 1539 G_SIGNAL_RUN_LAST, 1540 0, 1541 NULL, NULL, NULL, 1542 G_TYPE_NONE, 0); 1543 1544 /** 1545 * ShellApp:state: 1546 * 1547 * The high-level state of the application, effectively whether it's 1548 * running or not, or transitioning between those states. 1549 */ 1550 g_object_class_install_property (gobject_class, 1551 PROP_STATE, 1552 g_param_spec_enum ("state", 1553 "State", 1554 "Application state", 1555 SHELL_TYPE_APP_STATE, 1556 SHELL_APP_STATE_STOPPED, 1557 G_PARAM_READABLE)); 1558 1559 /** 1560 * ShellApp:id: 1561 * 1562 * The id of this application (a desktop filename, or a special string 1563 * like window:0xabcd1234) 1564 */ 1565 g_object_class_install_property (gobject_class, 1566 PROP_ID, 1567 g_param_spec_string ("id", 1568 "Application id", 1569 "The desktop file id of this ShellApp", 1570 NULL, 1571 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); 1572 1573 /** 1574 * ShellApp:action-group: 1575 * 1576 * The #GDBusActionGroup associated with this ShellApp, if any. See the 1577 * documentation of #GApplication and #GActionGroup for details. 1578 */ 1579 g_object_class_install_property (gobject_class, 1580 PROP_ACTION_GROUP, 1581 g_param_spec_object ("action-group", 1582 "Application Action Group", 1583 "The action group exported by the remote application", 1584 G_TYPE_ACTION_GROUP, 1585 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); 1586 /** 1587 * ShellApp:menu: 1588 * 1589 * The #GMenuProxy associated with this ShellApp, if any. See the 1590 * documentation of #GMenuModel for details. 1591 */ 1592 g_object_class_install_property (gobject_class, 1593 PROP_MENU, 1594 g_param_spec_object ("menu", 1595 "Application Menu", 1596 "The primary menu exported by the remote application", 1597 G_TYPE_MENU_MODEL, 1598 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); 1599 1600 }