diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 0c2b9759873463bfe42173a00201cde816aa4d2c..e6571555fd6a1bf93abcbac094e6448b4b5682e6 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -1023,7 +1023,8 @@ var BaseAppView = GObject.registerClass({ } vfunc_unmap() { - this._swipeTracker.enabled = false; + if (this._swipeTracker) + this._swipeTracker.enabled = false; this._clearAnimateLater(); this._disconnectDnD(); super.vfunc_unmap(); diff --git a/js/ui/calendar.js b/js/ui/calendar.js index 710efbac450806e014e2359a835f4f4dd622b97e..bb3ef1537a3b7b603d4bbcfad95744cb5460a576 100644 --- a/js/ui/calendar.js +++ b/js/ui/calendar.js @@ -912,8 +912,7 @@ class DoNotDisturbSwitch extends PopupMenu.Switch { Gio.SettingsBindFlags.INVERT_BOOLEAN); this.connect('destroy', () => { - this._settings.run_dispose(); - this._settings = null; + Gio.Settings.unbind(this._settings, 'show-banners'); }); } }); @@ -996,6 +995,15 @@ class CalendarMessageList extends St.Widget { this._addSection(this._notificationSection); Main.sessionMode.connect('updated', this._sync.bind(this)); + global.connect('closing', this._onClosing.bind(this)); + } + + _onClosing() { + for (const section of this._sectionList) { + for (const id of section._calendarConnectionsIds) + section.disconnect(id); + } + this._sectionList.remove_all_children(); } _addSection(section) { @@ -1014,6 +1022,7 @@ class CalendarMessageList extends St.Widget { this._sectionList.remove_actor(section); })); + section._calendarConnectionsIds = connectionsIds; this._sectionList.add_actor(section); } diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js index 0272c0e2338ba4987703505c5aa47b34b8cc7449..c31d856db37994fcdc5c9b39bcd7ebe7bb276160 100644 --- a/js/ui/iconGrid.js +++ b/js/ui/iconGrid.js @@ -371,6 +371,15 @@ var IconGridLayout = GObject.registerClass({ this._iconSizeUpdateResolveCbs = []; this._childrenMaxSize = -1; + this._container = null; + + global.connect('closing', this._onClosing.bind(this)); + } + + _onClosing() { + for (const item of this._items.keys()) + this._unlinkItem(item); + this._clearContainer(); } _findBestIconSize() { @@ -722,9 +731,15 @@ var IconGridLayout = GObject.registerClass({ } } - vfunc_set_container(container) { - if (this._container) + _clearContainer() { + if (this._container) { this._container.disconnect(this._containerDestroyedId); + this._container = null; + } + } + + vfunc_set_container(container) { + this._clearContainer(); this._container = container; diff --git a/js/ui/layout.js b/js/ui/layout.js index 70ece6cab8870b2f29ef62ca6cbfac5c212b2c83..03207a75b0455ee87024aa62eb447a84841ece12 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -216,6 +216,18 @@ var LayoutManager = GObject.registerClass({ global.stage.add_child(this.uiGroup); + global.connect_after('closing', () => { + Main.uiGroup.remove_child(global.window_group); + global.stage.add_child(global.window_group); + + Main.uiGroup.remove_child(global.top_window_group); + global.stage.add_child(global.top_window_group); + + while (Main.uiGroup.get_children().length > 0) + Main.uiGroup.get_children()[0].destroy(); + Main.uiGroup.destroy(); + }); + global.stage.remove_actor(global.window_group); this.uiGroup.add_actor(global.window_group); diff --git a/js/ui/messageList.js b/js/ui/messageList.js index 8ffdaebc133c76efaacc7bcd86e0c249f41f32e2..921033f80ff9d941d94cfe571a3e0ab9cfd08219 100644 --- a/js/ui/messageList.js +++ b/js/ui/messageList.js @@ -566,6 +566,7 @@ var MessageListSection = GObject.registerClass({ let id = Main.sessionMode.connect('updated', this._sync.bind(this)); + global.connect('closing', this._onClosing.bind(this)); this.connect('destroy', () => { Main.sessionMode.disconnect(id); }); @@ -575,6 +576,11 @@ var MessageListSection = GObject.registerClass({ this._sync(); } + _onClosing() { + for (const message of this._messages) + this.removeMessage(message, false); + } + get empty() { return this._empty; } diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js index 72542b38138ade82c7bbe248ddfa40aea7332381..05349a5558b7f862016a46051dff13b20f73a2c8 100644 --- a/js/ui/overviewControls.js +++ b/js/ui/overviewControls.js @@ -112,7 +112,8 @@ class ControlsManagerLayout extends Clutter.BoxLayout { vfunc_set_container(container) { this._container = container; - this.hookup_style(container); + if (container) + this.hookup_style(container); } vfunc_allocate(container, box) { @@ -603,6 +604,7 @@ class ControlsManager extends St.Widget { } _onDestroy() { + delete this._workspacesDisplay; global.workspace_manager.disconnect(this._nWorkspacesNotifyId); } @@ -620,7 +622,7 @@ class ControlsManager extends St.Widget { } vfunc_unmap() { - this._workspacesDisplay.hide(); + this._workspacesDisplay?.hide(); super.vfunc_unmap(); } diff --git a/js/ui/panel.js b/js/ui/panel.js index cba32416a21d15619bd79b7ed2a53ef1eb9514e3..af3a3a3456f909272f3ef1e79df72c9957701247 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -775,6 +775,13 @@ class Panel extends St.Widget { global.display.connect('workareas-changed', () => this.queue_relayout()); this._updatePanel(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + for (const role in PANEL_ITEM_IMPLEMENTATIONS) + this.statusArea[role].destroy(); } vfunc_get_preferred_width(_forHeight) { @@ -1009,6 +1016,9 @@ class Panel extends St.Widget { return null; } indicator = new constructor(this); + indicator.connect('destroy', () => { + indicator.menu?.disconnect(indicator.menu._openChangedId); + }); this.statusArea[role] = indicator; } return indicator; diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js index 0c2478f8db96a3e22396b94377231095bbe936ee..0d46cd95aa3a738f93fbfee7eff3c21e198b3ae6 100644 --- a/js/ui/panelMenu.js +++ b/js/ui/panelMenu.js @@ -116,15 +116,30 @@ var Button = GObject.registerClass({ this.track_hover = sensitive; } + _clearMenu() { + if (!this.menu) + return; + + if (this._openStateChangedId) + this.menu.disconnect(this._openStateChangedId); + if (this._keyPressEventId) + this.menu.actor.disconnect(this._keyPressEventId); + this._openStateChangedId = undefined; + this._keyPressEventId = undefined; + this.menu.destroy(); + this.menu = null; + } + setMenu(menu) { - if (this.menu) - this.menu.destroy(); + this._clearMenu(); this.menu = menu; if (this.menu) { this.menu.actor.add_style_class_name('panel-menu'); - this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); - this.menu.actor.connect('key-press-event', this._onMenuKeyPress.bind(this)); + this._openStateChangedId = + this.menu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); + this._keyPressEventId = + this.menu.actor.connect('key-press-event', this._onMenuKeyPress.bind(this)); Main.uiGroup.add_actor(this.menu.actor); this.menu.actor.hide(); @@ -185,8 +200,7 @@ var Button = GObject.registerClass({ } _onDestroy() { - if (this.menu) - this.menu.destroy(); + this._clearMenu(); super._onDestroy(); } }); diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 11528560d41e443a0ded781301fd06a36cabc513..62dc1574e10b6f78f61e090465f025b484a16be8 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -379,6 +379,12 @@ var PopupSwitchMenuItem = GObject.registerClass({ this._statusBin.child = this._switch; } + destroy() { + this._statusLabel.destroy(); + this._switch.destroy(); + super.destroy(); + } + setStatus(text) { if (text != null) { this._statusLabel.text = text; @@ -465,6 +471,8 @@ var PopupMenuBase = class { throw new TypeError('Cannot instantiate abstract class %s'.format(this.constructor.name)); this.sourceActor = sourceActor; + if (sourceActor) + this.sourceActor.connect('destroy', () => delete this.sourceActor); this.focusActor = sourceActor; this._parent = null; @@ -473,6 +481,7 @@ var PopupMenuBase = class { x_expand: true, y_expand: true, }); + this.box.connect('destroy', () => delete this.box); if (styleClass !== undefined) this.box.style_class = styleClass; @@ -773,6 +782,9 @@ var PopupMenuBase = class { } _getMenuItems() { + if (!this.box) + return []; + return this.box.get_children().map(a => a._delegate).filter(item => { return item instanceof PopupBaseMenuItem || item instanceof PopupMenuSection; }); @@ -808,7 +820,7 @@ var PopupMenuBase = class { destroy() { this.close(); this.removeAll(); - this.actor.destroy(); + this.actor?.destroy(); this.emit('destroy'); @@ -826,6 +838,10 @@ var PopupMenu = class extends PopupMenuBase { this._arrowSide = arrowSide; this._boxPointer = new BoxPointer.BoxPointer(arrowSide); + this._boxPointer.connect('destroy', () => { + this._boxPointer = null; + this.actor = null; + }); this.actor = this._boxPointer; this.actor._delegate = this; this.actor.style_class = 'popup-menu-boxpointer'; @@ -844,6 +860,11 @@ var PopupMenu = class extends PopupMenuBase { if (!this.sourceActor.mapped) this.close(); }); + this.sourceActor.connect('destroy', + () => { + this._keyPressId = 0; + this._notifyMappedId = 0; + }); } this._systemModalOpenedId = 0; @@ -941,7 +962,7 @@ var PopupMenu = class extends PopupMenuBase { if (this._activeMenuItem) this._activeMenuItem.active = false; - if (this._boxPointer.visible) { + if (this._boxPointer?.visible) { this._boxPointer.close(animate, () => { this.emit('menu-closed'); }); @@ -1150,6 +1171,7 @@ var PopupMenuSection = class extends PopupMenuBase { super(); this.actor = this.box; + this.actor.connect('destroy', () => delete this.actor); this.actor._delegate = this; this.isOpen = true; } @@ -1336,9 +1358,9 @@ var PopupMenuManager = class { menu.disconnect(menudata.destroyId); if (menudata.enterId) - menu.sourceActor.disconnect(menudata.enterId); + menu.sourceActor?.disconnect(menudata.enterId); if (menudata.focusInId) - menu.sourceActor.disconnect(menudata.focusInId); + menu.sourceActor?.disconnect(menudata.focusInId); if (menu.sourceActor) this._grabHelper.removeActor(menu.sourceActor); diff --git a/js/ui/searchController.js b/js/ui/searchController.js index 5d25897c7957745377ae857e3ac8cbad7b5813c1..80f7d7c7110cc8aed9cafa20c225754c51a7b987 100644 --- a/js/ui/searchController.js +++ b/js/ui/searchController.js @@ -68,7 +68,9 @@ var SearchController = GObject.registerClass({ this._searchResults.popupMenuDefault(); }); this._entry.connect('notify::mapped', this._onMapped.bind(this)); - global.stage.connect('notify::key-focus', this._onStageKeyFocusChanged.bind(this)); + const keyFocusId = + global.stage.connect('notify::key-focus', this._onStageKeyFocusChanged.bind(this)); + this.connect('destroy', () => global.stage.disconnect(keyFocusId)); this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon', diff --git a/js/ui/status/network.js b/js/ui/status/network.js index bb119f690c1fc82edd80f08e1c27b4b5fabbfe84..4cbf99b58cfc8efa26d5be760e0c6b0f78dc6cf0 100644 --- a/js/ui/status/network.js +++ b/js/ui/status/network.js @@ -119,17 +119,27 @@ var NMConnectionItem = class { this._sync(); } - _buildUI() { - this.labelItem = new PopupMenu.PopupMenuItem(''); + _setLabelItem(item) { + this.labelItem = item; this.labelItem.connect('activate', this._toggle.bind(this)); + this.labelItem.connect('destroy', () => delete this.labelItem); + } - this.radioItem = new PopupMenu.PopupMenuItem(this._connection.get_id(), false); + _setRadioItem(item) { + this.radioItem = item; this.radioItem.connect('activate', this._activate.bind(this)); + this.radioItem.connect('destroy', () => delete this.radioItem); + } + + _buildUI() { + this._setLabelItem(new PopupMenu.PopupMenuItem('')); + this._setRadioItem(new PopupMenu.PopupMenuItem(this._connection.get_id(), false)); } destroy() { - this.labelItem.destroy(); - this.radioItem.destroy(); + this.labelItem?.destroy(); + this.radioItem?.destroy(); + this._clearActiveConnection(); } updateForConnection(connection) { @@ -183,11 +193,16 @@ var NMConnectionItem = class { this._sync(); } - setActiveConnection(activeConnection) { + _clearActiveConnection() { if (this._activeConnectionChangedId > 0) { this._activeConnection.disconnect(this._activeConnectionChangedId); this._activeConnectionChangedId = 0; + this._activeConnection = null; } + } + + setActiveConnection(activeConnection) { + this._clearActiveConnection(); this._activeConnection = activeConnection; @@ -217,6 +232,7 @@ var NMConnectionSection = class NMConnectionSection { this.item = new PopupMenu.PopupSubMenuMenuItem('', true); this.item.menu.addMenuItem(this._labelSection); this.item.menu.addMenuItem(this._radioSection); + this.item.connect('destroy', () => delete this.item); this._notifyConnectivityId = this._client.connect('notify::connectivity', this._iconChanged.bind(this)); } @@ -227,7 +243,7 @@ var NMConnectionSection = class NMConnectionSection { this._notifyConnectivityId = 0; } - this.item.destroy(); + this.item?.destroy(); } _iconChanged() { @@ -361,6 +377,8 @@ var NMConnectionDevice = class NMConnectionDevice extends NMConnectionSection { } destroy() { + this.emit('destroy'); + if (this._stateChangedId) { GObject.signal_handler_disconnect(this._device, this._stateChangedId); this._stateChangedId = 0; @@ -490,6 +508,7 @@ var NMConnectionDevice = class NMConnectionDevice extends NMConnectionSection { } } }; +Signals.addSignalMethods(NMConnectionDevice.prototype); var NMDeviceWired = class extends NMConnectionDevice { constructor(client, device) { @@ -1204,6 +1223,7 @@ var NMDeviceWireless = class { this.item = new PopupMenu.PopupSubMenuMenuItem('', true); this.item.menu.addAction(_("Select Network"), this._showDialog.bind(this)); + this.item.connect('destroy', () => delete this.item); this._toggleItem = new PopupMenu.PopupMenuItem(''); this._toggleItem.connect('activate', this._toggleWifi.bind(this)); @@ -1230,6 +1250,8 @@ var NMDeviceWireless = class { } destroy() { + this.emit('destroy'); + if (this._activeApChangedId) { GObject.signal_handler_disconnect(this._device, this._activeApChangedId); this._activeApChangedId = 0; @@ -1259,7 +1281,7 @@ var NMDeviceWireless = class { this._notifyConnectivityId = 0; } - this.item.destroy(); + this.item?.destroy(); } _deviceStateChanged(device, newstate, oldstate, reason) { @@ -1415,12 +1437,14 @@ var NMVpnConnectionItem = class extends NMConnectionItem { return this._activeConnection.vpn_state != NM.VpnConnectionState.DISCONNECTED; } - _buildUI() { - this.labelItem = new PopupMenu.PopupMenuItem(''); - this.labelItem.connect('activate', this._toggle.bind(this)); + destroy() { + this._clearActiveConnection(); + super.destroy(); + } - this.radioItem = new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false); - this.radioItem.connect('toggled', this._toggle.bind(this)); + _buildUI() { + this._setLabelItem(new PopupMenu.PopupMenuItem('')); + this._setRadioItem(new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false)); } _sync() { @@ -1466,11 +1490,15 @@ var NMVpnConnectionItem = class extends NMConnectionItem { super._connectionStateChanged(); } - setActiveConnection(activeConnection) { + _clearActiveConnection() { if (this._activeConnectionChangedId > 0) { this._activeConnection.disconnect(this._activeConnectionChangedId); this._activeConnectionChangedId = 0; } + } + + setActiveConnection(activeConnection) { + this._clearActiveConnection(); this._activeConnection = activeConnection; @@ -1503,6 +1531,12 @@ var NMVpnSection = class extends NMConnectionSection { this._sync(); } + destroy() { + for (const item of this._connectionItems.values()) + item.destroy(); + super.destroy(); + } + _sync() { let nItems = this._connectionItems.size; this.item.visible = nItems > 0; @@ -1655,6 +1689,18 @@ class Indicator extends PanelMenu.SystemIndicator { this._ctypes[NM.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN; this._getClient(); + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + for (const device of this._nmDevices) + device._delegate?.destroy(); + this._vpnSection.destroy(); + + if (this._mainConnectionStateChangedId > 0) { + this._mainConnection.disconnect(this._mainConnectionStateChangedId); + this._mainConnectionStateChangedId = 0; + } } async _getClient() { @@ -1780,15 +1826,20 @@ class Indicator extends PanelMenu.SystemIndicator { let wrapperClass = this._dtypes[device.get_device_type()]; if (wrapperClass) { let wrapper = new wrapperClass(this._client, device); + const notifyInterfaceId = + device.connect('notify::interface', () => { + this._deviceChanged(device, false); + }); + wrapper.connect('destroy', + () => { + GObject.signal_handler_disconnect(device, notifyInterfaceId); + }); + device._delegate = wrapper; this._addDeviceWrapper(wrapper); this._nmDevices.push(device); this._deviceChanged(device, skipSyncDeviceNames); - - device.connect('notify::interface', () => { - this._deviceChanged(device, false); - }); } } diff --git a/src/main.c b/src/main.c index 5d07a430148b8ce607e63d4142e86895607af4bb..ca6d233c846e08b1bedc945d1a5d5c5949a82f1b 100644 --- a/src/main.c +++ b/src/main.c @@ -153,62 +153,6 @@ shell_fonts_init (void) cogl_pango_font_map_set_use_mipmapping (fontmap, FALSE); } -static void -shell_profiler_init (void) -{ - ShellGlobal *global; - GjsProfiler *profiler; - GjsContext *context; - const char *enabled; - const char *fd_str; - int fd = -1; - - /* Sysprof uses the "GJS_TRACE_FD=N" environment variable to connect GJS - * profiler data to the combined Sysprof capture. Since we are in control of - * the GjsContext, we need to proxy this FD across to the GJS profiler. - */ - - fd_str = g_getenv ("GJS_TRACE_FD"); - enabled = g_getenv ("GJS_ENABLE_PROFILER"); - if (fd_str == NULL || enabled == NULL) - return; - - global = shell_global_get (); - g_return_if_fail (global); - - context = _shell_global_get_gjs_context (global); - g_return_if_fail (context); - - profiler = gjs_context_get_profiler (context); - g_return_if_fail (profiler); - - if (fd_str) - { - fd = atoi (fd_str); - - if (fd > 2) - { - gjs_profiler_set_fd (profiler, fd); - gjs_profiler_start (profiler); - } - } -} - -static void -shell_profiler_shutdown (void) -{ - ShellGlobal *global; - GjsProfiler *profiler; - GjsContext *context; - - global = shell_global_get (); - context = _shell_global_get_gjs_context (global); - profiler = gjs_context_get_profiler (context); - - if (profiler) - gjs_profiler_stop (profiler); -} - static void malloc_statistics_callback (ShellPerfLog *perf_log, gpointer data) @@ -369,7 +313,7 @@ list_modes (const char *option_name, gpointer data, GError **error) { - ShellGlobal *global; + ShellGlobalSingleton *global; GjsContext *context; const char *script; int status; @@ -383,8 +327,7 @@ list_modes (const char *option_name, g_log_set_default_handler (shut_up, NULL); gtk_init_check (NULL, NULL); - _shell_global_init (NULL); - global = shell_global_get (); + global = _shell_create_global_singleton (NULL); context = _shell_global_get_gjs_context (global); shell_introspection_init (); @@ -394,7 +337,8 @@ list_modes (const char *option_name, if (!gjs_context_eval (context, script, -1, "
", &status, NULL)) g_message ("Retrieving list of available modes failed."); - g_object_unref (context); + _shell_global_destroy (global); + exit (status); } @@ -439,9 +383,9 @@ GOptionEntry gnome_shell_options[] = { int main (int argc, char **argv) { - GOptionContext *ctx; - GError *error = NULL; - int ecode; + g_autoptr (ShellGlobalSingleton) global = NULL; + g_autoptr (GOptionContext) ctx = NULL; + g_autoptr (GError) error = NULL; bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); @@ -455,11 +399,9 @@ main (int argc, char **argv) if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("%s: %s\n", argv[0], error->message); - exit (1); + return 1; } - g_option_context_free (ctx); - meta_plugin_manager_set_plugin_type (gnome_shell_plugin_get_type ()); meta_set_wm_name (WM_NAME); @@ -487,7 +429,7 @@ main (int argc, char **argv) if (session_mode == NULL) session_mode = is_gdm_mode ? (char *)"gdm" : (char *)"user"; - _shell_global_init ("session-mode", session_mode, NULL); + global = _shell_create_global_singleton ("session-mode", session_mode, NULL); dump_gjs_stack_on_signal (SIGABRT); dump_gjs_stack_on_signal (SIGFPE); @@ -500,13 +442,11 @@ main (int argc, char **argv) dump_gjs_stack_on_signal (SIGSEGV); } - shell_profiler_init (); - ecode = meta_run (); - shell_profiler_shutdown (); - + meta_start (); + meta_run_main_loop (); + shell_global_emit_closing (global); + meta_finalize (); g_debug ("Doing final cleanup"); - _shell_global_destroy_gjs_context (shell_global_get ()); - g_object_unref (shell_global_get ()); - return ecode; + return meta_get_exit_code (); } diff --git a/src/run-js-test.c b/src/run-js-test.c index ba5e875e8736b2bb1147fc252e5d1d247e53f186..126c7c842859c6ad4456ff1410d386edadda59e5 100644 --- a/src/run-js-test.c +++ b/src/run-js-test.c @@ -46,13 +46,13 @@ static GOptionEntry entries[] = { int main(int argc, char **argv) { + g_autoptr (ShellGlobalSingleton) global = NULL; + g_autoptr (GError) error = NULL; + g_autofree char *script = NULL; + g_autofree char *title = NULL; GOptionContext *context; - GError *error = NULL; - ShellGlobal *global; GjsContext *js_context; - char *script; const char *filename; - char *title; gsize len; int code; @@ -62,13 +62,14 @@ main(int argc, char **argv) g_option_context_set_ignore_unknown_options (context, TRUE); g_option_context_add_main_entries (context, entries, NULL); - if (!g_option_context_parse (context, &argc, &argv, &error)) + if (!g_option_context_parse (context, &argc, &argv, &error)) { g_error ("option parsing failed: %s", error->message); + g_clear_error (&error); + } setlocale (LC_ALL, ""); - _shell_global_init (NULL); - global = shell_global_get (); + global = _shell_create_global_singleton (NULL); js_context = _shell_global_get_gjs_context (global); /* prepare command line arguments */ @@ -76,7 +77,7 @@ main(int argc, char **argv) argc - 2, (const char**)argv + 2, &error)) { g_printerr ("Failed to defined ARGV: %s", error->message); - exit (1); + return 1; } if (command != NULL) { @@ -88,31 +89,25 @@ main(int argc, char **argv) len = strlen (script); filename = ""; } else /*if (argc >= 2)*/ { - error = NULL; if (!g_file_get_contents (argv[1], &script, &len, &error)) { g_printerr ("%s\n", error->message); - exit (1); + return 1; } filename = argv[1]; } title = g_filename_display_basename (filename); g_set_prgname (title); - g_free (title); /* evaluate the script */ error = NULL; if (!gjs_context_eval (js_context, script, len, filename, &code, &error)) { - g_free (script); g_printerr ("%s\n", error->message); - exit (1); } gjs_context_gc (js_context); gjs_context_gc (js_context); - g_object_unref (js_context); - g_free (script); - exit (code); + return code; } diff --git a/src/shell-global-private.h b/src/shell-global-private.h index 9969691cb47db09ae5c81f043cc609e80f4aa645..f993220488bb67319f9ecf2a9617a705f678c63c 100644 --- a/src/shell-global-private.h +++ b/src/shell-global-private.h @@ -6,12 +6,15 @@ #include -void _shell_global_init (const char *first_property_name, - ...); +typedef ShellGlobal ShellGlobalSingleton; + +ShellGlobalSingleton *_shell_create_global_singleton (const char *first_property_name, + ...); + void _shell_global_set_plugin (ShellGlobal *global, MetaPlugin *plugin); -void _shell_global_destroy_gjs_context (ShellGlobal *global); +void _shell_global_destroy (ShellGlobal *global); GjsContext *_shell_global_get_gjs_context (ShellGlobal *global); @@ -20,4 +23,8 @@ gboolean _shell_global_check_xdnd_event (ShellGlobal *global, void _shell_global_locate_pointer (ShellGlobal *global); +void shell_global_emit_closing (ShellGlobal *global); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (ShellGlobalSingleton, _shell_global_destroy) + #endif /* __SHELL_GLOBAL_PRIVATE_H__ */ diff --git a/src/shell-global.c b/src/shell-global.c index 027c9d6c45b649aee8cc0936772ad9ec9647f52f..364c632b3a6248317db678c0bb1bc2ed966efa0c 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -120,6 +120,7 @@ enum { NOTIFY_ERROR, LOCATE_POINTER, + CLOSING, LAST_SIGNAL }; @@ -334,6 +335,39 @@ switcheroo_vanished_cb (GDBusConnection *connection, g_object_notify (G_OBJECT (global), "switcheroo-control"); } +static void +shell_global_profiler_init (ShellGlobal *global) +{ + GjsProfiler *profiler; + const char *enabled; + const char *fd_str; + int fd = -1; + + /* Sysprof uses the "GJS_TRACE_FD=N" environment variable to connect GJS + * profiler data to the combined Sysprof capture. Since we are in control of + * the GjsContext, we need to proxy this FD across to the GJS profiler. + */ + + fd_str = g_getenv ("GJS_TRACE_FD"); + enabled = g_getenv ("GJS_ENABLE_PROFILER"); + if (fd_str == NULL || enabled == NULL) + return; + + profiler = gjs_context_get_profiler (global->js_context); + g_return_if_fail (profiler); + + if (fd_str) + { + fd = atoi (fd_str); + + if (fd > 2) + { + gjs_profiler_set_fd (profiler, fd); + gjs_profiler_start (profiler); + } + } +} + static void shell_global_init (ShellGlobal *global) { @@ -436,29 +470,52 @@ shell_global_init (ShellGlobal *global) switcheroo_vanished_cb, global, NULL); + + shell_global_profiler_init (global); +} + +void +shell_global_emit_closing (ShellGlobal *global) +{ + g_signal_emit (the_object, shell_global_signals[CLOSING], 0); } static void -shell_global_finalize (GObject *object) +destroy_gjs_context (GjsContext *context) { - ShellGlobal *global = SHELL_GLOBAL (object); + g_object_run_dispose (G_OBJECT (context)); + g_object_unref (context); +} - g_clear_object (&global->js_context); - g_object_unref (global->settings); +static void +shell_global_dispose (GObject *object) +{ + ShellGlobal *global = SHELL_GLOBAL (object); - the_object = NULL; + g_clear_pointer (&global->js_context, destroy_gjs_context); g_cancellable_cancel (global->switcheroo_cancellable); g_clear_object (&global->switcheroo_cancellable); + g_clear_object (&global->settings); g_clear_object (&global->userdatadir_path); g_clear_object (&global->runtime_state_path); + G_OBJECT_CLASS (shell_global_parent_class)->dispose (object); +} + +static void +shell_global_finalize (GObject *object) +{ + ShellGlobal *global = SHELL_GLOBAL (object); + + the_object = NULL; + g_free (global->session_mode); g_free (global->imagedir); g_free (global->userdatadir); - g_hash_table_unref (global->save_ops); + g_hash_table_destroy (global->save_ops); G_OBJECT_CLASS(shell_global_parent_class)->finalize (object); } @@ -470,6 +527,7 @@ shell_global_class_init (ShellGlobalClass *klass) gobject_class->get_property = shell_global_get_property; gobject_class->set_property = shell_global_set_property; + gobject_class->dispose = shell_global_dispose; gobject_class->finalize = shell_global_finalize; shell_global_signals[NOTIFY_ERROR] = @@ -488,6 +546,13 @@ shell_global_class_init (ShellGlobalClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + shell_global_signals[CLOSING] = + g_signal_new ("closing", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); g_object_class_install_property (gobject_class, PROP_SESSION_MODE, @@ -624,12 +689,12 @@ shell_global_class_init (ShellGlobalClass *klass) } /* - * _shell_global_init: (skip) + * _shell_global_new: (skip) * @first_property_name: the name of the first property * @...: the value of the first property, followed optionally by more * name/value pairs, followed by %NULL * - * Initializes the shell global singleton with the construction-time + * Creates and initializes the shell global singleton with the construction-time * properties. * * There are currently no such properties, so @first_property_name should @@ -637,14 +702,16 @@ shell_global_class_init (ShellGlobalClass *klass) * * This call must be called before shell_global_get() and shouldn't be called * more than once. + * + * Return value: (transfer none): the singleton #ShellGlobal object */ -void -_shell_global_init (const char *first_property_name, - ...) +ShellGlobalSingleton * +_shell_create_global_singleton (const char *first_property_name, + ...) { va_list argument_list; - g_return_if_fail (the_object == NULL); + g_return_val_if_fail (the_object == NULL, the_object); va_start (argument_list, first_property_name); the_object = SHELL_GLOBAL (g_object_new_valist (SHELL_TYPE_GLOBAL, @@ -652,6 +719,7 @@ _shell_global_init (const char *first_property_name, argument_list)); va_end (argument_list); + return (ShellGlobalSingleton *) the_object; } /** @@ -668,17 +736,19 @@ shell_global_get (void) } /** - * _shell_global_destroy_gjs_context: (skip) + * _shell_global_destroy: (skip) * @self: global object * - * Destroys the GjsContext held by ShellGlobal, in order to break reference - * counting cycles. (The GjsContext holds a reference to ShellGlobal because - * it's available as window.global inside JS.) + * Destroys the #ShellGlobal, disposing child objects (such as the #GjsContext) + * in order to break reference counting cycles. + * The GjsContext holds a reference to ShellGlobal because it's available as + * window.global inside JS. */ void -_shell_global_destroy_gjs_context (ShellGlobal *self) +_shell_global_destroy (ShellGlobal *global) { - g_clear_object (&self->js_context); + g_object_run_dispose (G_OBJECT (global)); + g_object_unref (global); } static guint32 @@ -1014,17 +1084,17 @@ _shell_global_set_plugin (ShellGlobal *global, st_entry_set_cursor_func (entry_cursor_func, global); st_clipboard_set_selection (meta_display_get_selection (display)); - g_signal_connect (global->stage, "notify::width", - G_CALLBACK (global_stage_notify_width), global); - g_signal_connect (global->stage, "notify::height", - G_CALLBACK (global_stage_notify_height), global); + g_signal_connect_object (global->stage, "notify::width", + G_CALLBACK (global_stage_notify_width), global, 0); + g_signal_connect_object (global->stage, "notify::height", + G_CALLBACK (global_stage_notify_height), global, 0); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT, global_stage_before_paint, global, NULL); - g_signal_connect (global->stage, "after-paint", - G_CALLBACK (global_stage_after_paint), global); + g_signal_connect_object (global->stage, "after-paint", + G_CALLBACK (global_stage_after_paint), global, 0); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, global_stage_after_swap, @@ -1043,10 +1113,10 @@ _shell_global_set_plugin (ShellGlobal *global, "End of frame, possibly including swap time", ""); - g_signal_connect (global->stage, "notify::key-focus", - G_CALLBACK (focus_actor_changed), global); - g_signal_connect (global->meta_display, "notify::focus-window", - G_CALLBACK (focus_window_changed), global); + g_signal_connect_object (global->stage, "notify::key-focus", + G_CALLBACK (focus_actor_changed), global, 0); + g_signal_connect_object (global->meta_display, "notify::focus-window", + G_CALLBACK (focus_window_changed), global, 0); if (global->xdisplay) g_signal_connect_object (global->meta_display, "x11-display-closing", @@ -1054,8 +1124,8 @@ _shell_global_set_plugin (ShellGlobal *global, backend = meta_get_backend (); settings = meta_backend_get_settings (backend); - g_signal_connect (settings, "ui-scaling-factor-changed", - G_CALLBACK (ui_scaling_factor_changed), global); + g_signal_connect_object (settings, "ui-scaling-factor-changed", + G_CALLBACK (ui_scaling_factor_changed), global, 0); global->focus_manager = st_focus_manager_get_for_stage (global->stage); @@ -1234,14 +1304,14 @@ pre_exec_close_fds(void) void shell_global_reexec_self (ShellGlobal *global) { - GPtrArray *arr; + g_autoptr (GPtrArray) arr = NULL; gsize len; #if defined __linux__ || defined __sun - char *buf; + g_autofree char *buf = NULL; + g_autoptr (GError) error = NULL; char *buf_p; char *buf_end; - GError *error = NULL; if (!g_file_get_contents ("/proc/self/cmdline", &buf, &len, &error)) { @@ -1257,7 +1327,8 @@ shell_global_reexec_self (ShellGlobal *global) g_ptr_array_add (arr, NULL); #elif defined __OpenBSD__ - gchar **args, **args_p; + g_autoptr char **args = NULL; + gchar **args_p; gint mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1) @@ -1267,7 +1338,6 @@ shell_global_reexec_self (ShellGlobal *global) if (sysctl (mib, G_N_ELEMENTS (mib), args, &len, NULL, 0) == -1) { g_warning ("failed to get command line args: %d", errno); - g_free (args); return; } @@ -1278,7 +1348,7 @@ shell_global_reexec_self (ShellGlobal *global) g_ptr_array_add (arr, NULL); #elif defined __FreeBSD__ - char *buf; + g_autoptr char *buf = NULL; char *buf_p; char *buf_end; gint mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, getpid() }; @@ -1290,7 +1360,6 @@ shell_global_reexec_self (ShellGlobal *global) if (sysctl (mib, G_N_ELEMENTS (mib), buf, &len, NULL, 0) == -1) { g_warning ("failed to get command line args: %d", errno); - g_free (buf); return; } @@ -1317,12 +1386,6 @@ shell_global_reexec_self (ShellGlobal *global) execvp (arr->pdata[0], (char**)arr->pdata); g_warning ("failed to reexec: %s", g_strerror (errno)); - g_ptr_array_free (arr, TRUE); -#if defined __linux__ || defined __FreeBSD__ - g_free (buf); -#elif defined __OpenBSD__ - g_free (args); -#endif } /** @@ -1341,7 +1404,7 @@ shell_global_notify_error (ShellGlobal *global, const char *msg, const char *details) { - g_signal_emit_by_name (global, "notify-error", msg, details); + g_signal_emit (global, shell_global_signals[NOTIFY_ERROR], 0, msg, details); } /**