extension.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. import Meta from 'gi://Meta';
  2. import Clutter from 'gi://Clutter';
  3. import * as Main from 'resource:///org/gnome/shell/ui/main.js';
  4. import * as Config from 'resource:///org/gnome/shell/misc/config.js';
  5. import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
  6. import { update_from_old_settings } from './conveniences/settings_updater.js';
  7. import { PipelinesManager } from './conveniences/pipelines_manager.js';
  8. import { EffectsManager } from './conveniences/effects_manager.js';
  9. import { Connections } from './conveniences/connections.js';
  10. import { Settings } from './conveniences/settings.js';
  11. import { KEYS } from './conveniences/keys.js';
  12. import { PanelBlur } from './components/panel.js';
  13. import { OverviewBlur } from './components/overview.js';
  14. import { DashBlur } from './components/dash_to_dock.js';
  15. import { LockscreenBlur } from './components/lockscreen.js';
  16. import { AppFoldersBlur } from './components/appfolders.js';
  17. import { WindowListBlur } from './components/window_list.js';
  18. import { CoverflowAltTabBlur } from './components/coverflow_alt_tab.js';
  19. import { ApplicationsBlur } from './components/applications.js';
  20. import { ScreenshotBlur } from './components/screenshot.js';
  21. /// The main extension class, created when the GNOME Shell is loaded.
  22. export default class BlurMyShell extends Extension {
  23. /// Enables the extension.
  24. enable() {
  25. // add the extension to global to make it accessible to other extensions
  26. // create it first as it is very useful when debugging crashes
  27. global.blur_my_shell = this;
  28. // update from old settings, very important for hacks level specifically
  29. update_from_old_settings(this.getSettings());
  30. // create a Settings instance, to manage extension's preferences
  31. // it needs to be loaded before logging, as it checks for DEBUG
  32. this._settings = new Settings(KEYS, this.getSettings());
  33. this._log("enabling extension...");
  34. // create main extension Connections instance
  35. this._connection = new Connections;
  36. // store it in a global array
  37. this._connections = [this._connection];
  38. // create a global effects manager (to prevent RAM bleeding)
  39. this._effects_manager = new EffectsManager(this._connection);
  40. // create a global pipelines manager, that helps talking with preferences
  41. this._pipelines_manager = new PipelinesManager(this._settings);
  42. // create an instance of each component, with its associated Connections
  43. let init = () => {
  44. // create a Connections instance, to manage signals
  45. let connection = new Connections;
  46. // store it to keeps track of them globally
  47. this._connections.push(connection);
  48. return [connection, this._settings, this._effects_manager];
  49. };
  50. this._panel_blur = new PanelBlur(...init());
  51. this._dash_to_dock_blur = new DashBlur(...init());
  52. this._overview_blur = new OverviewBlur(...init());
  53. this._lockscreen_blur = new LockscreenBlur(...init());
  54. this._appfolder_blur = new AppFoldersBlur(...init());
  55. this._window_list_blur = new WindowListBlur(...init());
  56. this._coverflow_alt_tab_blur = new CoverflowAltTabBlur(...init());
  57. this._applications_blur = new ApplicationsBlur(...init());
  58. this._screenshot_blur = new ScreenshotBlur(...init());
  59. // connect each component to preferences change
  60. this._connect_to_settings();
  61. // enable the lockscreen blur, only one important in both `user` session and `unlock-dialog`
  62. if (this._settings.lockscreen.BLUR && !this._lockscreen_blur.enabled)
  63. this._lockscreen_blur.enable();
  64. // ensure we take the correct action for the current session mode
  65. this._user_session_mode_enabled = false;
  66. this._on_session_mode_changed(Main.sessionMode);
  67. // watch for changes to the session mode
  68. this._connection.connect(Main.sessionMode, 'updated',
  69. () => this._on_session_mode_changed(Main.sessionMode)
  70. );
  71. }
  72. /// Enables the components related to the user session (everything except lockscreen blur).
  73. _enable_user_session() {
  74. this._log("changing mode to user session...");
  75. // maybe disable clipped redraw
  76. this._update_clipped_redraws();
  77. // enable every component
  78. // if the shell is still starting up, wait for it to be entirely loaded;
  79. // this should prevent bugs like #136 and #137
  80. if (Main.layoutManager._startingUp) {
  81. this._connection.connect(
  82. Main.layoutManager,
  83. 'startup-complete',
  84. () => this._enable_components()
  85. );
  86. } else
  87. this._enable_components();
  88. // try to enable the components as soon as possible anyway, this way the
  89. // overview may load before the user sees it
  90. try {
  91. if (this._settings.overview.BLUR && !this._overview_blur.enabled)
  92. this._overview_blur.enable();
  93. } catch (e) {
  94. this._log("Could not enable overview blur directly");
  95. this._log(e);
  96. }
  97. try {
  98. if (this._settings.dash_to_dock.BLUR
  99. && !this._dash_to_dock_blur.enabled)
  100. this._dash_to_dock_blur.enable();
  101. } catch (e) {
  102. this._log("Could not enable dash-to-dock blur directly");
  103. this._log(e);
  104. }
  105. try {
  106. if (this._settings.panel.BLUR && !this._panel_blur.enabled)
  107. this._panel_blur.enable();
  108. } catch (e) {
  109. this._log("Could not enable panel blur directly");
  110. this._log(e);
  111. }
  112. // tells the extension we have enabled the user session components, so that we do not
  113. // disable them later if they were not even enabled to begin with
  114. this._user_session_mode_enabled = true;
  115. }
  116. /// Disables the extension.
  117. ///
  118. /// This extension needs to use the 'unlock-dialog' session mode in order to change the blur on
  119. /// the lockscreen. We have kind of two states of enablement for this extension:
  120. /// - the 'enabled' state, which means that we have created the necessary components (which only
  121. /// are js objects) and enabled the lockscreen blur (which means swapping two functions from
  122. /// the `UnlockDialog` constructor with our ones;
  123. /// - the 'user session enabled` mode, which means that we are in the 'enabled' mode AND we are
  124. /// in the user mode, and so we enable all the other components that we created before.
  125. /// We switch from one state to the other thanks to `this._on_session_mode_changed`, and we
  126. /// track wether or not we are in the user mode with `this._user_session_mode_enabled` (because
  127. /// `this._on_session_mode_changed` might be called multiple times while in the user session
  128. /// mode, typically when going back from simple lockscreen and not sleep mode).
  129. disable() {
  130. this._log("disabling extension...");
  131. // disable every component from user session mode
  132. if (this._user_session_mode_enabled)
  133. this._disable_user_session();
  134. this._overview_blur.restore_patched_proto();
  135. // disable lockscreen blur too
  136. this._lockscreen_blur.disable();
  137. // untrack them
  138. this._panel_blur = null;
  139. this._dash_to_dock_blur = null;
  140. this._overview_blur = null;
  141. this._appfolder_blur = null;
  142. this._lockscreen_blur = null;
  143. this._window_list_blur = null;
  144. this._coverflow_alt_tab_blur = null;
  145. this._applications_blur = null;
  146. this._screenshot_blur = null;
  147. // make sure no settings change can re-enable them
  148. this._settings.disconnect_all_settings();
  149. // force disconnecting every signal, even if component crashed
  150. this._connections.forEach((connections) => {
  151. connections.disconnect_all();
  152. });
  153. this._connections = [];
  154. // remove the clipped redraws flag
  155. this._reenable_clipped_redraws();
  156. // remove the extension from GJS's global
  157. delete global.blur_my_shell;
  158. this._log("extension disabled.");
  159. this._settings = null;
  160. }
  161. /// Disables the components related to the user session (everything except lockscreen blur).
  162. _disable_user_session() {
  163. this._log("disabling user session mode...");
  164. // disable every component except lockscreen blur
  165. this._panel_blur.disable();
  166. this._dash_to_dock_blur.disable();
  167. this._overview_blur.disable();
  168. this._appfolder_blur.disable();
  169. this._window_list_blur.disable();
  170. this._coverflow_alt_tab_blur.disable();
  171. this._applications_blur.disable();
  172. this._screenshot_blur.disable();
  173. // remove the clipped redraws flag
  174. this._reenable_clipped_redraws();
  175. // tells the extension we have disabled the user session components, so that we do not
  176. // disable them later again if they were already disabled
  177. this._user_session_mode_enabled = false;
  178. }
  179. /// Restarts the components related to the user session.
  180. _restart() {
  181. this._log("restarting...");
  182. this._disable_user_session();
  183. this._enable_user_session();
  184. this._log("restarted.");
  185. }
  186. /// Changes the extension to operate either on 'user' mode or 'unlock-dialog' mode, switching
  187. /// from one to the other means enabling/disabling every component except lockscreen blur.
  188. _on_session_mode_changed(session) {
  189. if (session.currentMode === 'user' || session.parentMode === 'user') {
  190. if (!this._user_session_mode_enabled)
  191. // we need to activate everything
  192. this._enable_user_session();
  193. }
  194. else if (session.currentMode === 'unlock-dialog') {
  195. if (this._user_session_mode_enabled)
  196. // we need to disable the components related to the user session mode
  197. this._disable_user_session();
  198. }
  199. }
  200. /// Add or remove the clutter debug flag to disable clipped redraws.
  201. /// This will entirely fix the blur effect, but should not be used except if
  202. /// the user really needs it, as clipped redraws are a huge performance
  203. /// boost for the compositor.
  204. _update_clipped_redraws() {
  205. if (this._settings.HACKS_LEVEL === 2)
  206. this._disable_clipped_redraws();
  207. else
  208. this._reenable_clipped_redraws();
  209. }
  210. /// Add the Clutter debug flag.
  211. _disable_clipped_redraws() {
  212. let gnome_shell_major_version = parseInt(Config.PACKAGE_VERSION.split('.')[0]);
  213. if (gnome_shell_major_version >= 48)
  214. Clutter.add_debug_flags(
  215. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  216. );
  217. else
  218. Meta.add_clutter_debug_flags(
  219. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  220. );
  221. }
  222. /// Remove the Clutter debug flag.
  223. _reenable_clipped_redraws() {
  224. let gnome_shell_major_version = parseInt(Config.PACKAGE_VERSION.split('.')[0]);
  225. if (gnome_shell_major_version >= 48)
  226. Clutter.remove_debug_flags(
  227. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  228. );
  229. else
  230. Meta.remove_clutter_debug_flags(
  231. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  232. );
  233. }
  234. /// Enables every component from the user session needed, should be called when the shell is
  235. /// entirely loaded as the `enable` methods interact with it.
  236. _enable_components() {
  237. // enable each component if needed, and if it is not already enabled
  238. if (this._settings.panel.BLUR && !this._panel_blur.enabled)
  239. this._panel_blur.enable();
  240. if (this._settings.dash_to_dock.BLUR && !this._dash_to_dock_blur.enabled)
  241. this._dash_to_dock_blur.enable();
  242. if (this._settings.overview.BLUR && !this._overview_blur.enabled)
  243. this._overview_blur.enable();
  244. if (this._settings.appfolder.BLUR)
  245. this._appfolder_blur.enable();
  246. if (this._settings.applications.BLUR)
  247. this._applications_blur.enable();
  248. if (this._settings.window_list.BLUR)
  249. this._window_list_blur.enable();
  250. if (this._settings.coverflow_alt_tab.BLUR)
  251. this._coverflow_alt_tab_blur.enable();
  252. if (this._settings.screenshot.BLUR)
  253. this._screenshot_blur.enable();
  254. this._log("all components enabled.");
  255. }
  256. /// Updates needed things in each component when a preference changed
  257. _connect_to_settings() {
  258. // restart the extension when hacks level is changed, easier than
  259. // restarting individual components and should not happen often either
  260. this._settings.HACKS_LEVEL_changed(() => this._restart());
  261. // ---------- OVERVIEW ----------
  262. // toggled on/off
  263. this._settings.overview.BLUR_changed(() => {
  264. if (this._settings.overview.BLUR)
  265. this._overview_blur.enable();
  266. else
  267. this._overview_blur.disable();
  268. });
  269. // overview pipeline changed
  270. this._settings.overview.PIPELINE_changed(() => {
  271. if (this._settings.overview.BLUR)
  272. this._overview_blur.update_backgrounds();
  273. });
  274. // overview components style changed
  275. this._settings.overview.STYLE_COMPONENTS_changed(() => {
  276. if (this._settings.overview.BLUR)
  277. this._overview_blur.update_components_classname();
  278. });
  279. // ---------- APPFOLDER ----------
  280. // toggled on/off
  281. this._settings.appfolder.BLUR_changed(() => {
  282. if (this._settings.appfolder.BLUR)
  283. this._appfolder_blur.enable();
  284. else
  285. this._appfolder_blur.disable();
  286. });
  287. // appfolder sigma changed
  288. this._settings.appfolder.SIGMA_changed(() => {
  289. if (this._settings.appfolder.BLUR)
  290. this._appfolder_blur.set_sigma(
  291. this._settings.appfolder.SIGMA
  292. );
  293. });
  294. // appfolder brightness changed
  295. this._settings.appfolder.BRIGHTNESS_changed(() => {
  296. if (this._settings.appfolder.BLUR)
  297. this._appfolder_blur.set_brightness(
  298. this._settings.appfolder.BRIGHTNESS
  299. );
  300. });
  301. // appfolder dialogs style changed
  302. this._settings.appfolder.STYLE_DIALOGS_changed(() => {
  303. if (this._settings.appfolder.BLUR)
  304. this._appfolder_blur.blur_appfolders();
  305. });
  306. // ---------- PANEL ----------
  307. // toggled on/off
  308. this._settings.panel.BLUR_changed(() => {
  309. if (this._settings.panel.BLUR)
  310. this._panel_blur.enable();
  311. else
  312. this._panel_blur.disable();
  313. });
  314. // static blur toggled on/off, really we can just reload the blur at this point
  315. this._settings.panel.STATIC_BLUR_changed(() => {
  316. if (this._settings.panel.BLUR)
  317. this._panel_blur.reset();
  318. });
  319. // panel pipeline changed
  320. this._settings.panel.PIPELINE_changed(() => {
  321. if (this._settings.panel.BLUR)
  322. this._panel_blur.update_pipeline();
  323. });
  324. // panel blur's overview connection toggled on/off
  325. this._settings.panel.UNBLUR_IN_OVERVIEW_changed(() => {
  326. this._panel_blur.connect_to_windows_and_overview();
  327. });
  328. // force light text toggled on/off
  329. this._settings.panel.FORCE_LIGHT_TEXT_changed(() => {
  330. if (this._settings.panel.BLUR)
  331. this._panel_blur.update_light_text_classname();
  332. });
  333. // panel override background toggled on/off
  334. this._settings.panel.OVERRIDE_BACKGROUND_changed(() => {
  335. if (this._settings.panel.BLUR)
  336. this._panel_blur.connect_to_windows_and_overview();
  337. });
  338. // panel style changed
  339. this._settings.panel.STYLE_PANEL_changed(() => {
  340. if (this._settings.panel.BLUR)
  341. this._panel_blur.connect_to_windows_and_overview();
  342. });
  343. // panel background's dynamic overriding toggled on/off
  344. this._settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_changed(() => {
  345. if (this._settings.panel.BLUR)
  346. this._panel_blur.connect_to_windows_and_overview();
  347. });
  348. // ---------- DASH TO DOCK ----------
  349. // toggled on/off
  350. this._settings.dash_to_dock.BLUR_changed(() => {
  351. if (this._settings.dash_to_dock.BLUR)
  352. this._dash_to_dock_blur.enable();
  353. else
  354. this._dash_to_dock_blur.disable();
  355. });
  356. // static blur toggled on/off
  357. this._settings.dash_to_dock.STATIC_BLUR_changed(() => {
  358. if (this._settings.dash_to_dock.BLUR)
  359. this._dash_to_dock_blur.change_blur_type();
  360. });
  361. // overview pipeline changed
  362. this._settings.dash_to_dock.PIPELINE_changed(() => {
  363. if (this._settings.dash_to_dock.BLUR)
  364. this._dash_to_dock_blur.update_pipeline();
  365. });
  366. // dash-to-dock override background toggled on/off
  367. this._settings.dash_to_dock.OVERRIDE_BACKGROUND_changed(() => {
  368. if (this._settings.dash_to_dock.BLUR)
  369. this._dash_to_dock_blur.update_background();
  370. });
  371. // dash-to-dock style changed
  372. this._settings.dash_to_dock.STYLE_DASH_TO_DOCK_changed(() => {
  373. if (this._settings.dash_to_dock.BLUR)
  374. this._dash_to_dock_blur.update_background();
  375. });
  376. // dash-to-dock blur's overview connection toggled on/off
  377. this._settings.dash_to_dock.UNBLUR_IN_OVERVIEW_changed(() => {
  378. if (this._settings.dash_to_dock.BLUR)
  379. this._dash_to_dock_blur.connect_to_overview();
  380. });
  381. // ---------- APPLICATIONS ----------
  382. // toggled on/off
  383. this._settings.applications.BLUR_changed(() => {
  384. if (this._settings.applications.BLUR)
  385. this._applications_blur.enable();
  386. else
  387. this._applications_blur.disable();
  388. });
  389. // application opacity changed
  390. this._settings.applications.OPACITY_changed(() => {
  391. if (this._settings.applications.BLUR)
  392. this._applications_blur.set_opacity(
  393. this._settings.applications.OPACITY
  394. );
  395. });
  396. // application dynamic-opacity changed
  397. this._settings.applications.DYNAMIC_OPACITY_changed(() => {
  398. if (this._settings.applications.BLUR)
  399. this._applications_blur.init_dynamic_opacity();
  400. });
  401. // application blur-on-overview changed
  402. this._settings.applications.BLUR_ON_OVERVIEW_changed(() => {
  403. if (this._settings.applications.BLUR)
  404. this._applications_blur.connect_to_overview();
  405. });
  406. // application enable-all changed
  407. this._settings.applications.ENABLE_ALL_changed(() => {
  408. if (this._settings.applications.BLUR)
  409. this._applications_blur.update_all_windows();
  410. });
  411. // application whitelist changed
  412. this._settings.applications.WHITELIST_changed(() => {
  413. if (
  414. this._settings.applications.BLUR
  415. && !this._settings.applications.ENABLE_ALL
  416. )
  417. this._applications_blur.update_all_windows();
  418. });
  419. // application blacklist changed
  420. this._settings.applications.BLACKLIST_changed(() => {
  421. if (
  422. this._settings.applications.BLUR
  423. && this._settings.applications.ENABLE_ALL
  424. )
  425. this._applications_blur.update_all_windows();
  426. });
  427. // ---------- LOCKSCREEN ----------
  428. // toggled on/off
  429. this._settings.lockscreen.BLUR_changed(() => {
  430. if (this._settings.lockscreen.BLUR)
  431. this._lockscreen_blur.enable();
  432. else
  433. this._lockscreen_blur.disable();
  434. });
  435. // lockscreen pipeline changed
  436. this._settings.lockscreen.PIPELINE_changed(() => {
  437. if (this._settings.lockscreen.BLUR)
  438. this._lockscreen_blur.update_lockscreen();
  439. });
  440. // ---------- WINDOW LIST ----------
  441. // toggled on/off
  442. this._settings.window_list.BLUR_changed(() => {
  443. if (this._settings.window_list.BLUR)
  444. this._window_list_blur.enable();
  445. else
  446. this._window_list_blur.disable();
  447. });
  448. // ---------- COVERFLOW ALT-TAB ----------
  449. // toggled on/off
  450. this._settings.coverflow_alt_tab.BLUR_changed(() => {
  451. if (this._settings.coverflow_alt_tab.BLUR)
  452. this._coverflow_alt_tab_blur.enable();
  453. else
  454. this._coverflow_alt_tab_blur.disable();
  455. });
  456. // ---------- HIDETOPBAR ----------
  457. // toggled on/off
  458. this._settings.hidetopbar.COMPATIBILITY_changed(() => {
  459. // no need to verify if it is enabled or not, it is done anyway
  460. this._panel_blur.connect_to_windows_and_overview();
  461. });
  462. // ---------- DASH TO PANEL ----------
  463. // toggled on/off
  464. this._settings.dash_to_panel.BLUR_ORIGINAL_PANEL_changed(() => {
  465. if (this._settings.panel.BLUR)
  466. this._panel_blur.reset();
  467. });
  468. // ---------- SCREENSHOT ----------
  469. // toggled on/off
  470. this._settings.screenshot.BLUR_changed(() => {
  471. if (this._settings.screenshot.BLUR)
  472. this._screenshot_blur.enable();
  473. else
  474. this._screenshot_blur.disable();
  475. });
  476. // screenshot pipeline changed
  477. this._settings.screenshot.PIPELINE_changed(() => {
  478. if (this._settings.screenshot.BLUR)
  479. this._screenshot_blur.update_pipeline();
  480. });
  481. }
  482. _log(str) {
  483. if (this._settings.DEBUG)
  484. console.log(`[Blur my Shell > extension] ${str}`);
  485. }
  486. }