extension.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  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 { EffectsManager } from './conveniences/effects_manager.js';
  6. import { Connections } from './conveniences/connections.js';
  7. import { Settings } from './conveniences/settings.js';
  8. import { Keys } from './conveniences/keys.js';
  9. import { PanelBlur } from './components/panel.js';
  10. import { OverviewBlur } from './components/overview.js';
  11. import { DashBlur } from './components/dash_to_dock.js';
  12. import { LockscreenBlur } from './components/lockscreen.js';
  13. import { AppFoldersBlur } from './components/appfolders.js';
  14. import { WindowListBlur } from './components/window_list.js';
  15. import { ApplicationsBlur } from './components/applications.js';
  16. import { ScreenshotBlur } from './components/screenshot.js';
  17. // This lists the components that need to be connected in order to either use
  18. // general sigma/brightness or their own.
  19. const INDEPENDENT_COMPONENTS = [
  20. "overview", "appfolder", "panel", "dash_to_dock", "applications",
  21. "lockscreen", "window_list", "screenshot"
  22. ];
  23. /// The main extension class, created when the GNOME Shell is loaded.
  24. export default class BlurMyShell extends Extension {
  25. /// Enables the extension
  26. enable() {
  27. // add the extension to global to make it accessible to other extensions
  28. // create it first as it is very useful when debugging crashes
  29. global.blur_my_shell = this;
  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 an instance of each component, with its associated Connections
  41. let init = _ => {
  42. // create a Connections instance, to manage signals
  43. let connection = new Connections;
  44. // store it to keeps track of them globally
  45. this._connections.push(connection);
  46. return [connection, this._settings, this._effects_manager];
  47. };
  48. this._panel_blur = new PanelBlur(...init());
  49. this._dash_to_dock_blur = new DashBlur(...init());
  50. this._overview_blur = new OverviewBlur(...init());
  51. this._lockscreen_blur = new LockscreenBlur(...init());
  52. this._appfolder_blur = new AppFoldersBlur(...init());
  53. this._window_list_blur = new WindowListBlur(...init());
  54. this._applications_blur = new ApplicationsBlur(...init());
  55. this._screenshot_blur = new ScreenshotBlur(...init());
  56. // maybe disable clipped redraw
  57. this._update_clipped_redraws();
  58. // connect each component to preferences change
  59. this._connect_to_settings();
  60. // enable every component
  61. // if the shell is still starting up, wait for it to be entirely loaded;
  62. // this should prevent bugs like #136 and #137
  63. if (Main.layoutManager._startingUp) {
  64. this._connection.connect(
  65. Main.layoutManager,
  66. 'startup-complete',
  67. this._enable_components.bind(this)
  68. );
  69. } else {
  70. this._enable_components();
  71. }
  72. // try to enable the components as soon as possible anyway, this way the
  73. // overview may load before the user sees it
  74. try {
  75. if (this._settings.overview.BLUR && !this._overview_blur.enabled)
  76. this._overview_blur.enable();
  77. } catch (e) {
  78. this._log("Could not enable overview blur directly");
  79. this._log(e);
  80. }
  81. try {
  82. if (this._settings.dash_to_dock.BLUR
  83. && !this._dash_to_dock_blur.enabled)
  84. this._dash_to_dock_blur.enable();
  85. } catch (e) {
  86. this._log("Could not enable dash-to-dock blur directly");
  87. this._log(e);
  88. }
  89. try {
  90. if (this._settings.panel.BLUR && !this._panel_blur.enabled)
  91. this._panel_blur.enable();
  92. } catch (e) {
  93. this._log("Could not enable panel blur directly");
  94. this._log(e);
  95. }
  96. }
  97. /// Disables the extension
  98. disable() {
  99. this._log("disabling extension...");
  100. // disable every component
  101. this._panel_blur.disable();
  102. this._dash_to_dock_blur.disable();
  103. this._overview_blur.disable();
  104. this._lockscreen_blur.disable();
  105. this._appfolder_blur.disable();
  106. this._window_list_blur.disable();
  107. this._applications_blur.disable();
  108. this._screenshot_blur.disable();
  109. // untrack them
  110. this._panel_blur = null;
  111. this._dash_to_dock_blur = null;
  112. this._overview_blur = null;
  113. this._lockscreen_blur = null;
  114. this._appfolder_blur = null;
  115. this._window_list_blur = null;
  116. this._applications_blur = null;
  117. // make sure no settings change can re-enable them
  118. this._settings.disconnect_all_settings();
  119. // force disconnecting every signal, even if component crashed
  120. this._connections.forEach((connections) => {
  121. connections.disconnect_all();
  122. });
  123. this._connections = [];
  124. // remove the clipped redraws flag
  125. this._reenable_clipped_redraws();
  126. // remove the extension from GJS's global
  127. delete global.blur_my_shell;
  128. this._log("extension disabled.");
  129. this._settings = null;
  130. }
  131. /// Restart the extension.
  132. _restart() {
  133. this._log("restarting...");
  134. this.disable();
  135. this.enable();
  136. this._log("restarted.");
  137. }
  138. /// Add or remove the clutter debug flag to disable clipped redraws.
  139. /// This will entirely fix the blur effect, but should not be used except if
  140. /// the user really needs it, as clipped redraws are a huge performance
  141. /// boost for the compositor.
  142. _update_clipped_redraws() {
  143. if (this._settings.HACKS_LEVEL === 3)
  144. this._disable_clipped_redraws();
  145. else
  146. this._reenable_clipped_redraws();
  147. }
  148. /// Add the Clutter debug flag.
  149. _disable_clipped_redraws() {
  150. Meta.add_clutter_debug_flags(
  151. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  152. );
  153. }
  154. /// Remove the Clutter debug flag.
  155. _reenable_clipped_redraws() {
  156. Meta.remove_clutter_debug_flags(
  157. null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
  158. );
  159. }
  160. /// Enables every component needed, should be called when the shell is
  161. /// entirely loaded as the `enable` methods interact with it.
  162. _enable_components() {
  163. // enable each component if needed, and if it is not already enabled
  164. if (this._settings.panel.BLUR && !this._panel_blur.enabled)
  165. this._panel_blur.enable();
  166. if (this._settings.dash_to_dock.BLUR && !this._dash_to_dock_blur.enabled)
  167. this._dash_to_dock_blur.enable();
  168. if (this._settings.overview.BLUR && !this._overview_blur.enabled)
  169. this._overview_blur.enable();
  170. if (this._settings.lockscreen.BLUR)
  171. this._lockscreen_blur.enable();
  172. if (this._settings.appfolder.BLUR)
  173. this._appfolder_blur.enable();
  174. if (this._settings.applications.BLUR)
  175. this._applications_blur.enable();
  176. if (this._settings.window_list.BLUR)
  177. this._window_list_blur.enable();
  178. if (this._settings.screenshot.BLUR)
  179. this._screenshot_blur.enable();
  180. this._log("all components enabled.");
  181. }
  182. /// Updates needed things in each component when a preference changed
  183. _connect_to_settings() {
  184. // global blur values changed, update everybody
  185. this._settings.SIGMA_changed(() => {
  186. this._update_sigma();
  187. });
  188. this._settings.BRIGHTNESS_changed(() => {
  189. this._update_brightness();
  190. });
  191. this._settings.COLOR_changed(() => {
  192. this._update_color();
  193. });
  194. this._settings.NOISE_AMOUNT_changed(() => {
  195. this._update_noise_amount();
  196. });
  197. this._settings.NOISE_LIGHTNESS_changed(() => {
  198. this._update_noise_lightness();
  199. });
  200. this._settings.COLOR_AND_NOISE_changed(() => {
  201. // both updating noise amount and color calls `update_enabled` on
  202. // each color and noise effects
  203. this._update_noise_amount();
  204. this._update_color();
  205. });
  206. // restart the extension when hacks level is changed, easier than
  207. // restarting individual components and should not happen often either
  208. this._settings.HACKS_LEVEL_changed(_ => this._restart());
  209. // connect each component to use the proper sigma/brightness/color
  210. INDEPENDENT_COMPONENTS.forEach(component => {
  211. this._connect_to_individual_settings(component);
  212. });
  213. // other component's preferences changed
  214. // ---------- OVERVIEW ----------
  215. // toggled on/off
  216. this._settings.overview.BLUR_changed(() => {
  217. if (this._settings.overview.BLUR) {
  218. this._overview_blur.enable();
  219. } else {
  220. this._overview_blur.disable();
  221. }
  222. });
  223. // overview components style changed
  224. this._settings.overview.STYLE_COMPONENTS_changed(() => {
  225. if (this._settings.overview.BLUR) {
  226. this._overview_blur.update_components_classname();
  227. }
  228. });
  229. // ---------- APPFOLDER ----------
  230. // toggled on/off
  231. this._settings.appfolder.BLUR_changed(() => {
  232. if (this._settings.appfolder.BLUR) {
  233. this._appfolder_blur.enable();
  234. } else {
  235. this._appfolder_blur.disable();
  236. }
  237. });
  238. // appfolder dialogs style changed
  239. this._settings.appfolder.STYLE_DIALOGS_changed(() => {
  240. if (this._settings.appfolder.BLUR)
  241. this._appfolder_blur.blur_appfolders();
  242. });
  243. // ---------- PANEL ----------
  244. // toggled on/off
  245. this._settings.panel.BLUR_changed(() => {
  246. if (this._settings.panel.BLUR) {
  247. this._panel_blur.enable();
  248. } else {
  249. this._panel_blur.disable();
  250. }
  251. });
  252. this._settings.COLOR_AND_NOISE_changed(() => {
  253. // permits making sure that the blur is not washed out when disabling
  254. // the other effects
  255. if (this._settings.panel.BLUR)
  256. this._panel_blur.invalidate_all_blur();
  257. });
  258. // static blur toggled on/off
  259. this._settings.panel.STATIC_BLUR_changed(() => {
  260. if (this._settings.panel.BLUR)
  261. this._panel_blur.update_all_blur_type();
  262. });
  263. // panel blur's overview connection toggled on/off
  264. this._settings.panel.UNBLUR_IN_OVERVIEW_changed(() => {
  265. this._panel_blur.connect_to_windows_and_overview();
  266. });
  267. // panel override background toggled on/off
  268. this._settings.panel.OVERRIDE_BACKGROUND_changed(() => {
  269. if (this._settings.panel.BLUR)
  270. this._panel_blur.connect_to_windows_and_overview();
  271. });
  272. // panel style changed
  273. this._settings.panel.STYLE_PANEL_changed(() => {
  274. if (this._settings.panel.BLUR)
  275. this._panel_blur.connect_to_windows_and_overview();
  276. });
  277. // panel background's dynamic overriding toggled on/off
  278. this._settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_changed(() => {
  279. if (this._settings.panel.BLUR)
  280. this._panel_blur.connect_to_windows_and_overview();
  281. });
  282. // ---------- DASH TO DOCK ----------
  283. // toggled on/off
  284. this._settings.dash_to_dock.BLUR_changed(() => {
  285. if (this._settings.dash_to_dock.BLUR) {
  286. this._dash_to_dock_blur.enable();
  287. } else {
  288. this._dash_to_dock_blur.disable();
  289. }
  290. });
  291. // TODO implement static blur for dash
  292. // static blur toggled on/off
  293. this._settings.dash_to_dock.STATIC_BLUR_changed(() => {
  294. //if (this._settings.dash_to_dock.BLUR)
  295. // this._dash_to_dock_blur.change_blur_type();
  296. });
  297. // dash-to-dock override background toggled on/off
  298. this._settings.dash_to_dock.OVERRIDE_BACKGROUND_changed(() => {
  299. if (this._settings.dash_to_dock.BLUR)
  300. this._dash_to_dock_blur.update_background();
  301. });
  302. // dash-to-dock style changed
  303. this._settings.dash_to_dock.STYLE_DASH_TO_DOCK_changed(() => {
  304. if (this._settings.dash_to_dock.BLUR)
  305. this._dash_to_dock_blur.update_background();
  306. });
  307. // dash-to-dock blur's overview connection toggled on/off
  308. this._settings.dash_to_dock.UNBLUR_IN_OVERVIEW_changed(() => {
  309. if (this._settings.dash_to_dock.BLUR)
  310. this._dash_to_dock_blur.connect_to_overview();
  311. });
  312. // ---------- APPLICATIONS ----------
  313. // toggled on/off
  314. this._settings.applications.BLUR_changed(() => {
  315. if (this._settings.applications.BLUR) {
  316. this._applications_blur.enable();
  317. } else {
  318. this._applications_blur.disable();
  319. }
  320. });
  321. // application opacity changed
  322. this._settings.applications.OPACITY_changed(_ => {
  323. if (this._settings.applications.BLUR)
  324. this._applications_blur.set_opacity(
  325. this._settings.applications.OPACITY
  326. );
  327. });
  328. // application blur-on-overview changed
  329. this._settings.applications.BLUR_ON_OVERVIEW_changed(_ => {
  330. if (this._settings.applications.BLUR)
  331. this._applications_blur.connect_to_overview();
  332. });
  333. // application enable-all changed
  334. this._settings.applications.ENABLE_ALL_changed(_ => {
  335. if (this._settings.applications.BLUR)
  336. this._applications_blur.update_all_windows();
  337. });
  338. // application whitelist changed
  339. this._settings.applications.WHITELIST_changed(_ => {
  340. if (
  341. this._settings.applications.BLUR
  342. && !this._settings.applications.ENABLE_ALL
  343. )
  344. this._applications_blur.update_all_windows();
  345. });
  346. // application blacklist changed
  347. this._settings.applications.BLACKLIST_changed(_ => {
  348. if (
  349. this._settings.applications.BLUR
  350. && this._settings.applications.ENABLE_ALL
  351. )
  352. this._applications_blur.update_all_windows();
  353. });
  354. // ---------- LOCKSCREEN ----------
  355. // toggled on/off
  356. this._settings.lockscreen.BLUR_changed(() => {
  357. if (this._settings.lockscreen.BLUR) {
  358. this._lockscreen_blur.enable();
  359. } else {
  360. this._lockscreen_blur.disable();
  361. }
  362. });
  363. // ---------- WINDOW LIST ----------
  364. // toggled on/off
  365. this._settings.window_list.BLUR_changed(() => {
  366. if (this._settings.window_list.BLUR) {
  367. this._window_list_blur.enable();
  368. } else {
  369. this._window_list_blur.disable();
  370. }
  371. });
  372. // ---------- HIDETOPBAR ----------
  373. // toggled on/off
  374. this._settings.hidetopbar.COMPATIBILITY_changed(() => {
  375. // no need to verify if it is enabled or not, it is done anyway
  376. this._panel_blur.connect_to_windows_and_overview();
  377. });
  378. // ---------- DASH TO PANEL ----------
  379. // toggled on/off
  380. this._settings.dash_to_panel.BLUR_ORIGINAL_PANEL_changed(() => {
  381. if (this._settings.panel.BLUR)
  382. this._panel_blur.reset();
  383. });
  384. // ---------- SCREENSHOT ----------
  385. // toggled on/off
  386. this._settings.screenshot.BLUR_changed(() => {
  387. if (this._settings.screenshot.BLUR) {
  388. this._screenshot_blur.enable();
  389. } else {
  390. this._screenshot_blur.disable();
  391. }
  392. });
  393. }
  394. /// Select the component by its name and connect it to its preferences
  395. /// changes for general values, sigma and brightness.
  396. ///
  397. /// Doing this in such a way is less accessible but prevents a lot of
  398. /// boilerplate and headaches.
  399. _connect_to_individual_settings(name) {
  400. // get component and preferences needed
  401. let component = this['_' + name + '_blur'];
  402. let component_settings = this._settings[name];
  403. // general values switch is toggled
  404. component_settings.CUSTOMIZE_changed(() => {
  405. if (component_settings.CUSTOMIZE) {
  406. component.set_sigma(component_settings.SIGMA);
  407. component.set_brightness(component_settings.BRIGHTNESS);
  408. component.set_color(component_settings.COLOR);
  409. component.set_noise_amount(component_settings.NOISE_AMOUNT);
  410. component.set_noise_lightness(component_settings.NOISE_LIGHTNESS);
  411. }
  412. else {
  413. component.set_sigma(this._settings.SIGMA);
  414. component.set_brightness(this._settings.BRIGHTNESS);
  415. component.set_color(this._settings.COLOR);
  416. component.set_noise_amount(this._settings.NOISE_AMOUNT);
  417. component.set_noise_lightness(this._settings.NOISE_LIGHTNESS);
  418. }
  419. });
  420. // sigma is changed
  421. component_settings.SIGMA_changed(() => {
  422. if (component_settings.CUSTOMIZE)
  423. component.set_sigma(component_settings.SIGMA);
  424. else
  425. component.set_sigma(this._settings.SIGMA);
  426. });
  427. // brightness is changed
  428. component_settings.BRIGHTNESS_changed(() => {
  429. if (component_settings.CUSTOMIZE)
  430. component.set_brightness(component_settings.BRIGHTNESS);
  431. else
  432. component.set_brightness(this._settings.BRIGHTNESS);
  433. });
  434. // color is changed
  435. component_settings.COLOR_changed(() => {
  436. if (component_settings.CUSTOMIZE)
  437. component.set_color(component_settings.COLOR);
  438. else
  439. component.set_color(this._settings.COLOR);
  440. });
  441. // noise amount is changed
  442. component_settings.NOISE_AMOUNT_changed(() => {
  443. if (component_settings.CUSTOMIZE)
  444. component.set_noise_amount(component_settings.NOISE_AMOUNT);
  445. else
  446. component.set_noise_amount(this._settings.NOISE_AMOUNT);
  447. });
  448. // noise lightness is changed
  449. component_settings.NOISE_LIGHTNESS_changed(() => {
  450. if (component_settings.CUSTOMIZE)
  451. component.set_noise_lightness(component_settings.NOISE_LIGHTNESS);
  452. else
  453. component.set_noise_lightness(this._settings.NOISE_LIGHTNESS);
  454. });
  455. }
  456. /// Update each component's sigma value
  457. _update_sigma() {
  458. INDEPENDENT_COMPONENTS.forEach(name => {
  459. // get component and preferences needed
  460. let component = this['_' + name + '_blur'];
  461. let component_settings = this._settings[name];
  462. // update sigma accordingly
  463. if (component_settings.CUSTOMIZE) {
  464. component.set_sigma(component_settings.SIGMA);
  465. }
  466. else {
  467. component.set_sigma(this._settings.SIGMA);
  468. }
  469. });
  470. }
  471. /// Update each component's brightness value
  472. _update_brightness() {
  473. INDEPENDENT_COMPONENTS.forEach(name => {
  474. // get component and preferences needed
  475. let component = this['_' + name + '_blur'];
  476. let component_settings = this._settings[name];
  477. // update brightness accordingly
  478. if (component_settings.CUSTOMIZE)
  479. component.set_brightness(component_settings.BRIGHTNESS);
  480. else
  481. component.set_brightness(this._settings.BRIGHTNESS);
  482. });
  483. }
  484. /// Update each component's color value
  485. _update_color() {
  486. INDEPENDENT_COMPONENTS.forEach(name => {
  487. // get component and preferences needed
  488. let component = this['_' + name + '_blur'];
  489. let component_settings = this._settings[name];
  490. // update color accordingly
  491. if (component_settings.CUSTOMIZE)
  492. component.set_color(component_settings.COLOR);
  493. else
  494. component.set_color(this._settings.COLOR);
  495. });
  496. }
  497. /// Update each component's noise amount value
  498. _update_noise_amount() {
  499. INDEPENDENT_COMPONENTS.forEach(name => {
  500. // get component and preferences needed
  501. let component = this['_' + name + '_blur'];
  502. let component_settings = this._settings[name];
  503. // update color accordingly
  504. if (component_settings.CUSTOMIZE)
  505. component.set_noise_amount(component_settings.NOISE_AMOUNT);
  506. else
  507. component.set_noise_amount(this._settings.NOISE_AMOUNT);
  508. });
  509. }
  510. /// Update each component's noise lightness value
  511. _update_noise_lightness() {
  512. INDEPENDENT_COMPONENTS.forEach(name => {
  513. // get component and preferences needed
  514. let component = this['_' + name + '_blur'];
  515. let component_settings = this._settings[name];
  516. // update color accordingly
  517. if (component_settings.CUSTOMIZE)
  518. component.set_noise_lightness(component_settings.NOISE_LIGHTNESS);
  519. else
  520. component.set_noise_lightness(this._settings.NOISE_LIGHTNESS);
  521. });
  522. }
  523. _log(str) {
  524. if (this._settings.DEBUG)
  525. console.log(`[Blur my Shell > extension] ${str}`);
  526. }
  527. }