extension.js 21 KB

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