Repository: GregVido/mica-electron
Branch: main
Commit: 2caa79980dc5
Files: 23
Total size: 68.7 KB
Directory structure:
gitextract_n378x8h9/
├── .gitignore
├── LICENSE
├── README.md
├── build.bat
├── data
├── exemple.js
├── files/
│ ├── css/
│ │ ├── dark.css
│ │ └── styles.css
│ ├── index.html
│ └── js/
│ └── scripts.js
├── main.js
├── module/
│ ├── app.cpp
│ └── assets/
│ ├── dwm.cpp
│ ├── dwm.h
│ ├── types.h
│ ├── user32.cpp
│ ├── win.cpp
│ └── winstyle.h
├── package.json
├── src/
│ ├── micaElectron_arm64.node
│ ├── micaElectron_ia32.node
│ └── micaElectron_x64.node
└── types/
└── index.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
.vscode
bin
build/**
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
Mica Electron
Mica Electron is a tool to add mica effect on electron app.
This is created by GregVido. Mica electron is now compatible with windows 10
## ☕ Support the project
If you like **Mica Electron** and want to support its development, you can buy me a coffee ❤️
👉 https://www.buymeacoffee.com/gregvido
## Quickstart
```bash
$ npm install mica-electron
```
```js
const electron = require('electron');
const { PARAMS, VALUE, MicaBrowserWindow, IS_WINDOWS_11, WIN10 } = require('mica-electron');
const path = require('path');
electron.app.on('ready', () => {
const win = new MicaBrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
show: false,
// frame: false // -> now work, you can remove the frame properly !!
});
win.setDarkTheme();
win.setMicaEffect();
// win.alwaysFocused(true); // -> allows you to keep the mica effects even if the window is no focus (decrease performance)
win.loadFile(path.join(__dirname, 'files', 'index.html'));
win.webContents.once('dom-ready', () => {
win.show();
});
});
```
Apply effect for windows 11
You can apply different mica effect :
```js
win.setMicaEffect(); // Mica Effect
win.setMicaTabbedEffect(); // Mica Tabbed
win.setMicaAcrylicEffect(); // Acrylic for windows 11
```
Change theme for windows 11
You can change theme :
```js
win.setAutoTheme(); // Same theme as computer
win.setLightTheme(); // Force light theme
win.setDarkTheme(); // Force dark theme
```
Apply effect for windows 10
You can apply different blur effect :
```js
win.setTransparent(); // Transparent window
win.setBlur(); // Blurred window
win.setAcrylic(); // Acrylic window
```
Change radius
You can change corner radius :
```js
win.setRoundedCorner(); // Rounded
win.setSmallRoundedCorner(); // Small rounded
win.setSquareCorner(); // Square
```
Change window colors
You can change window colors :
```js
win.setBorderColor('#f40b0b'); // Border color
win.setBorderColor(null); // -> disable effect
win.setCaptionColor('#262626'); // Background titlebar color
win.setCaptionColor(null); // -> disable effect
win.setTitleTextColor('#fff'); // Title text color
win.setTitleTextColor(null); // -> disable effect
```
Change custom transparent effect for windows 10 NEW!
You can change window colors :
```js
win.setCustomEffect(WIN10.TRANSPARENT, '#34ebc0', 0.5); // Transparent
win.setCustomEffect(WIN10.ACRYLIC, '#34ebc0', 0.4); // Acrylic
```
## Source Install / Manual Compilation
To compile from source it's easiest to use
[`node-gyp`](https://github.com/TooTallNate/node-gyp):
``` bash
$ npm install -g node-gyp
```
Now you can compile `mica-electron`:
``` bash
$ cd .\node_modules\mica-electron\
$ ./build.bat
```
## Objects details
PARAMS Object
The params is a number, you can has an object to help you:
```js
const PARAMS = {
BACKGROUND: {
AUTO: 0,
NONE: 1,
ACRYLIC: 3, // Acrylic
MICA: 2, // Mica
TABBED_MICA: 4 // Mica tabbed
},
CORNER: 5,
BORDER_COLOR: 6,
CAPTION_COLOR: 7,
TEXT_COLOR: 8,
FRAME: 9
}
```
VALUE Object
The value is a number, you can has an object to help you:
```js
const VALUE = {
THEME: {
AUTO: 5, // select theme by the windows theme
DARK: 1, // select the dark theme
LIGHT: 2, // select the white theme
},
CORNER: {
DEFAULT: 0,
DONOTROUND: 1,
ROUND: 2,
ROUNDSMALL: 3
},
COLOR: {
RED: 0x000000FF,
GREEN: 0x0000FF00,
BLUE: 0x00FF0000,
BLACK: 0x00000000,
WHITE: 0x00FFFFFF,
FROM_RGB: (r, g, b) => {
return r + (g << 8) + (b << 16);
}
},
FALSE: 0,
TRUE: 1
}
```
IS_WINDOWS_11
IS_WINDOWS_11 is a boolean constant to detect the OS version. If it is true then it's a windows 11 computer, otherwise it is another version (10, 8, 7 ...)
WIN10 Object
The value is a number, you can has an object to help you:
```js
const WIN10 = {
TRANSPARENT: 2,
BLURBEHIND: 3, // didn't work on windows 11
ACRYLIC: 4
}
```
## FAQ
Error: '...\micaElectron.node' was compiled against a different Node.js version using ...
If you are an error of nodejs version, use electron-packager to rebuild the project with the good version.
```bash
$ npm install electron
$ npm install electron-rebuild
$ .\node_modules\.bin\electron-rebuild
```
Build for 32 bits ?
If you want use `mica-electron` with 32 bits electron app, rebuild C++ script
``` bash
$ cd .\node_modules\mica-electron\
$ node-gyp rebuild --arch=ia32
$ cd ..\..\
$ .\node_modules\.bin\electron-rebuild --arch=ia32
```
## Awesome applications using Mica-Electron
- [MicaDiscord](https://www.micadiscord.com/) by GregVido and Arbitro
- [Cider](https://github.com/ciderapp/Cider) by [Cider Collective](https://github.com/ciderapp)
- [Fluent Browser](https://github.com/ThePiGuy3141/fluent-browser) by ThePiGuy3141
- [Mica-Snap](https://github.com/GregVido/Mica-Snap) by GregVido
- [SysMocap](https://github.com/xianfei/SysMocap) by [xianfei](https://github.com/xianfei)
================================================
FILE: build.bat
================================================
@echo off
REM =========================
REM ANSI colors
REM =========================
for /f %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
set C_RESET=%ESC%[0m
set C_TITLE=%ESC%[96m
set C_INFO=%ESC%[94m
set C_OK=%ESC%[92m
set C_WARN=%ESC%[93m
set C_ERR=%ESC%[91m
set C_LINE=%ESC%[90m
set INCLUDE_ARM64=true
REM -------------------------
REM Parse arguments
REM -------------------------
for %%i in (%*) do (
if "%%i"=="--include-arm64" (
set INCLUDE_ARM64=true
)
)
echo %C_LINE%-------------------------%C_RESET%
echo %C_TITLE%MicaElectron Builder v1.1%C_RESET%
echo %C_LINE%-------------------------%C_RESET%
echo.
REM -------------------------
REM Safety checks
REM -------------------------
where node >nul 2>&1 || (
echo %C_ERR%[ERROR]%C_RESET% Node.js not found in PATH
exit /b 1
)
where node-gyp >nul 2>&1 || (
echo %C_ERR%[ERROR]%C_RESET% node-gyp not found in PATH
exit /b 1
)
if exist data (
ren data binding.gyp || (
echo %C_ERR%[ERROR]%C_RESET% Failed to rename data to binding.gyp
exit /b 1
)
)
if not exist binding.gyp (
echo %C_ERR%[ERROR]%C_RESET% binding.gyp not found
exit /b 1
)
REM -------------------------
REM Reset src folder
REM -------------------------
echo %C_LINE%-------------------------%C_RESET%
echo %C_INFO%Resetting src folder...%C_RESET%
if exist src (
rmdir /s /q src || (
echo %C_ERR%[ERROR]%C_RESET% Failed to remove src folder
goto restore_gyp
)
)
mkdir src || (
echo %C_ERR%[ERROR]%C_RESET% Failed to create src folder
goto restore_gyp
)
REM -------------------------
REM Build x64
REM -------------------------
echo %C_LINE%-------------------------%C_RESET%
node-gyp rebuild >nul 2>&1 | echo %C_INFO%Building x64...%C_RESET%
if errorlevel 1 (
echo %C_ERR%[ERROR]%C_RESET% x64 build failed
goto restore_gyp
)
if not exist build\Release\micaElectron.node (
echo %C_ERR%[ERROR]%C_RESET% x64 output file not found
goto restore_gyp
)
move build\Release\micaElectron.node src\micaElectron_x64.node >nul || (
echo %C_ERR%[ERROR]%C_RESET% Failed to move x64 binary
goto restore_gyp
)
echo %C_OK%[OK]%C_RESET% x64 build completed
echo.
REM -------------------------
REM Build ia32
REM -------------------------
echo %C_LINE%-------------------------%C_RESET%
node-gyp rebuild --arch=ia32 >nul 2>&1 | echo %C_INFO%Building ia32...%C_RESET%
if errorlevel 1 (
echo %C_ERR%[ERROR]%C_RESET% ia32 build failed
goto restore_gyp
)
if not exist build\Release\micaElectron.node (
echo %C_ERR%[ERROR]%C_RESET% ia32 output file not found
goto restore_gyp
)
move build\Release\micaElectron.node src\micaElectron_ia32.node >nul || (
echo %C_ERR%[ERROR]%C_RESET% Failed to move ia32 binary
goto restore_gyp
)
echo %C_OK%[OK]%C_RESET% ia32 build completed
echo.
REM -------------------------
REM Build ARM64 (optional)
REM -------------------------
if "%INCLUDE_ARM64%"=="true" (
echo %C_LINE%-------------------------%C_RESET%
node-gyp rebuild --arch=arm64 >nul 2>&1 | echo %C_INFO%Building arm64...%C_RESET%
if errorlevel 1 (
echo %C_ERR%[ERROR]%C_RESET% arm64 build failed
goto restore_gyp
)
if not exist build\Release\micaElectron.node (
echo %C_ERR%[ERROR]%C_RESET% arm64 output file not found
goto restore_gyp
)
move build\Release\micaElectron.node src\micaElectron_arm64.node >nul || (
echo %C_ERR%[ERROR]%C_RESET% Failed to move arm64 binary
goto restore_gyp
)
echo %C_OK%[OK]%C_RESET% arm64 build completed
echo.
)
REM -------------------------
REM Cleanup
REM -------------------------
echo %C_LINE%-------------------------%C_RESET%
echo %C_INFO%Cleaning build artifacts...%C_RESET%
if exist build (
rmdir /s /q build || (
echo %C_ERR%[ERROR]%C_RESET% Failed to remove build folder
goto restore_gyp
)
)
REM -------------------------
REM Restore binding.gyp
REM -------------------------
:restore_gyp
if exist binding.gyp (
ren binding.gyp data
)
echo %C_LINE%-------------------------%C_RESET%
echo %C_OK%Build finished successfully%C_RESET%
echo %C_LINE%-------------------------%C_RESET%
exit /b 0
================================================
FILE: data
================================================
{
"targets": [
{
"target_name": "micaElectron",
"sources": [ "module/app.cpp" ]
}
],
'variables':{
'openssl_fips':0
}
}
================================================
FILE: exemple.js
================================================
const { app, ipcMain } = require('electron');
const { PARAMS, VALUE, MicaBrowserWindow, IS_WINDOWS_11, WIN10 } = require('./main.js');
const path = require('path');
app.on('ready', () => {
const win = new MicaBrowserWindow({
width: 800,
height: 600,
autoHideMenuBar: true,
show: false,
// frame: false, // -> now work, you can remove the frame properly !!
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
win.setLightTheme();
if (IS_WINDOWS_11)
win.setMicaTabbedEffect();
else
win.setCustomEffect(WIN10.ACRYLIC, '#401896', .2);
// win.alwaysFocused(true); // -> allows you to keep the mica effects even if the window is no focus (decrease performance)
win.loadFile(path.join(__dirname, 'files', 'index.html'));
win.webContents.once('dom-ready', () => {
win.show();
});
// Change theme
ipcMain.on('theme', (evt, newValue) => {
switch (newValue) {
case VALUE.THEME.AUTO:
win.setAutoTheme();
break
case VALUE.THEME.LIGHT:
win.setLightTheme();
break
case VALUE.THEME.DARK:
win.setDarkTheme();
break
}
});
// Change effect
ipcMain.on('effect', (evt, newParams) => {
switch (newParams) {
case PARAMS.BACKGROUND.MICA:
win.setMicaEffect();
break
case PARAMS.BACKGROUND.TABBED_MICA:
win.setMicaTabbedEffect();
break
case PARAMS.BACKGROUND.ACRYLIC:
win.setMicaAcrylicEffect();
break
}
});
// apply effect (corner, background-color, ...)
ipcMain.on('params', (evt, params, value) => {
switch (params) {
case PARAMS.CORNER:
switch (value) {
case VALUE.CORNER.ROUND:
win.setRoundedCorner();
break
case VALUE.CORNER.ROUNDSMALL:
win.setSmallRoundedCorner();
break
case VALUE.CORNER.DONOTROUND:
win.setSquareCorner();
break
}
break
case PARAMS.BORDER_COLOR:
win.setBorderColor(value);
break
case PARAMS.CAPTION_COLOR:
win.setCaptionColor(value);
break
case PARAMS.TEXT_COLOR:
win.setTitleTextColor(value);
break
case 10:
switch (value) {
case 0:
win.setTransparent();
break
case 1:
win.setBlur();
break
case 2:
win.setAcrylic();
break
}
break
}
});
});
================================================
FILE: files/css/dark.css
================================================
main,
fieldset {
background-color: #000000e3;
border: 1px solid #353535;
color: #fff;
}
fieldset {
background-color: #00000096;
}
button {
background: #000;
border: 1px solid rgb(24, 24, 24);
color: #fff;
}
button:hover {
background: #141414;
}
/* Track */
::-webkit-scrollbar-track {
background: #888;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #f1f1f1;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #f8f8f8;
}
================================================
FILE: files/css/styles.css
================================================
body {
font-family: Arial, Helvetica, sans-serif;
background-color: transparent;
padding: 10px;
}
h1 {
-webkit-app-region: drag;
}
main,
fieldset {
margin: auto;
width: 60%;
text-align: center;
background-color: #ffffffe3;
padding: 10px;
border: 1px solid #cacaca;
border-radius: 15px;
color: #000;
}
fieldset {
margin-top: 10px;
background-color: #ffffff96;
}
legend {
text-align: left;
}
button {
background: #fff;
border: 1px solid rgb(231, 231, 231);
padding: 7px 15px;
transition: .2s background;
color: #000;
}
button:hover {
background: #ebebeb;
}
/* width */
::-webkit-scrollbar {
width: 4px;
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
================================================
FILE: files/index.html
================================================
Hello World
Hello World
================================================
FILE: files/js/scripts.js
================================================
const { ipcRenderer } = require('electron');
const darkTheme = document.createElement('link');
darkTheme.rel = 'stylesheet';
darkTheme.href = 'css/dark.css';
function setTheme(theme) {
ipcRenderer.send('theme', theme);
if (theme == 1)
document.head.appendChild(darkTheme);
else
document.head.removeChild(darkTheme);
}
function setEffect(effect) {
ipcRenderer.send('effect', effect);
}
function setParams(params, value) {
ipcRenderer.send('params', params, value);
}
function htmlToColor(color) {
let r = color.slice(0, 2);
let g = color.slice(2, 4);
let b = color.slice(4, 6);
return parseInt("0x" + b + g + r);
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
document.head[event.matches ? 'appendChild' : 'removeChild'](darkTheme);
});
window.onload = () => {
const colorBorder = document.getElementById('colorBorder');
const colorCaption = document.getElementById('colorCaption');
const colorTitle = document.getElementById('colorTitle');
colorBorder.addEventListener('input', () => {
setParams(6, colorBorder.value);
});
colorCaption.addEventListener('input', () => {
setParams(7, colorCaption.value);
});
colorTitle.addEventListener('input', () => {
setParams(8, colorTitle.value);
});
}
================================================
FILE: main.js
================================================
/*
Copyright 2024 GregVido
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const electron = require('electron');
const os = require('os');
const fs = require('fs');
const path = require('path');
const IS_ELECTRON_RECENT_VERSION = process.versions.electron >= '27.0.0' && process.versions.electron <= '40.0.0';
const filepath = path.join(__dirname, 'src', '/micaElectron_' + process.arch);
let executeDwm, redraw, executeUser32;
if (fs.existsSync(filepath + '.node')) {
/* If mica-electron is running on windows */
if (process.platform == 'win32') {
const micaBuild = require(filepath);
executeDwm = micaBuild.executeDwm;
redraw = micaBuild.redraw;
executeUser32 = micaBuild.executeUser32;
}
else {
console.log('Mica-Electron only works on Windows!');
console.log('Mica-Electron: Disabled');
}
}
else {
console.log('./src/micaElectron_' + process.arch + '.node does not exist!');
console.log('Mica-Electron: Disabled');
}
electron.app.commandLine.appendSwitch("enable-transparent-visuals");
/**
* Detect if OS is windows 11
* @returns {Boolean} True if windows 11
*/
function isWin11() {
const version = os.release().split('.');
if (version.length == 3)
return version[2] >= '22000';
return false;
}
const WINDOWS_11 = isWin11();
/**
* Remove a frame from a window
* @param {BrowserWindow} window Target to remove frame
*/
function redrawFrame(window) {
if (redraw) {
const HWND = window.getNativeWindowHandle()["readInt32LE"]();
const bounds = window.getBounds();
// executeDwm(HWND, PARAMS.FRAME, VALUE.FALSE);
redraw(HWND, bounds.x, bounds.y, bounds.width, bounds.height);
}
}
const PARAMS = {
BACKGROUND: {
AUTO: 0,
NONE: 1,
ACRYLIC: 3, // Acrylic
MICA: 2, // Mica
TABBED_MICA: 4 // Mica tabbed
},
CORNER: 5,
BORDER_COLOR: 6,
CAPTION_COLOR: 7,
TEXT_COLOR: 8,
FRAME: 9,
MARGIN: 10
}
const VALUE = {
THEME: {
AUTO: 5, // select theme by the windows theme
DARK: 1, // select the dark theme
LIGHT: 2, // select the white theme
},
CORNER: {
DEFAULT: 0,
DONOTROUND: 1,
ROUND: 2,
ROUNDSMALL: 3
},
COLOR: {
RED: 0x000000FF,
GREEN: 0x0000FF00,
BLUE: 0x00FF0000,
BLACK: 0x00000000,
WHITE: 0x00FFFFFF,
FROM_RGB: (r, g, b) => {
return r + (g << 8) + (b << 16);
}
},
FALSE: 0,
TRUE: 1
}
const WIN10 = {
TRANSPARENT: 2,
BLURBEHIND: 3,
ACRYLIC: 4
}
/**
* Convert HTML color to windows color
* @param {String} str HTML color
* @return {Number} windows color (int, 24 bits)
*/
let getColorByString = (str) => {
if (str.startsWith('#')) {
const hexs = str.slice(1);
if (hexs.length == 3) {
const r = parseInt(hexs[0] + hexs[0], 16);
const g = parseInt(hexs[1] + hexs[1], 16);
const b = parseInt(hexs[2] + hexs[2], 16);
return VALUE.COLOR.FROM_RGB(r, g, b);
}
else if (hexs.length == 6) {
const r = parseInt(hexs.slice(0, 2), 16);
const g = parseInt(hexs.slice(2, 4), 16);
const b = parseInt(hexs.slice(4, 6), 16);
return VALUE.COLOR.FROM_RGB(r, g, b);
}
}
else if (str.startsWith('rgb(')) {
const data = str.slice(4).split(')')[0].split(',');
const r = parseInt(data[0]);
const g = parseInt(data[1]);
const b = parseInt(data[2]);
return VALUE.COLOR.FROM_RGB(r, g, b);
}
else if (str.length == 8) { // color from getAccentColor()
const r = parseInt(str.slice(0, 2), 16);
const g = parseInt(str.slice(2, 4), 16);
const b = parseInt(str.slice(4, 6), 16);
return VALUE.COLOR.FROM_RGB(r, g, b);
}
}
class BrowserWindow extends electron.BrowserWindow {
/** @type {Number} */
effect = PARAMS.BACKGROUND.AUTO;
/** @type {Number} */
theme = VALUE.THEME.AUTO;
/** @type {Boolean} */
useDWM = false;
/** @type {Boolean} */
hasFrameless = false;
/** @type {Boolean} */
forceFocus = false;
/** @type {Boolean} */
hasMargin = false;
/**
* Create electron BrowserWindow with mica effect features
* @param {...Object} args
*/
constructor(...args) {
if (args.length > 0) {
args[0].transparent = false;
args[0].backgroundColor = '#00ffffff';
} else
args.push({
backgroundColor: '#00ffffff'
});
if (IS_ELECTRON_RECENT_VERSION)
args[0].transparent = true;
super(...args);
this.hasFrameless = args[0].frame === false || args[0].titleBarStyle == 'hidden';
let applyEffect = () => {
if (args.length > 0 && this.useDWM) {
this.executeDwm(this.effect, this.theme);
}
}
let frameRemoved = true;
let onWindowShow = () => {
applyEffect();
if (frameRemoved) {
frameRemoved = false;
setTimeout(() => {
this.hide();
if (IS_ELECTRON_RECENT_VERSION) {
this.interceptMessage();
this.applyStyle();
if (this.hasFrameless)
this.removeCaption();
}
redrawFrame(this);
this.show();
}, 60);
}
}
this.on('show', onWindowShow);
this.on('restore', onWindowShow);
this.on('close', () => {
if (this.marginTimer)
clearInterval(this.marginTimer);
});
this.on('resize', () => {
if (this.hasFrameless)
setTimeout(applyEffect, 60); // refresh effect
});
}
/**
* Enable resize/maximize for the window
*/
applyStyle() {
this.executeDwm(PARAMS.FRAME, 1);
}
/**
* Restore the full screen action
*/
interceptMessage() {
this.executeDwm(PARAMS.FRAME, 2);
}
/**
* Remove Window Caption
*/
removeCaption() {
this.executeDwm(PARAMS.FRAME, 4);
}
/**
* Disable transparent for mica effect
*/
disableMargin() {
if (this.marginTimer) {
clearInterval(this.marginTimer);
this.marginTimer = null;
}
if (this.useDWM)
this.executeDwm(PARAMS.MARGIN, 1);
this.hasMargin = false;
}
/**
* Enable transparent for mica effect
*/
enableMargin() {
if (this.marginTimer)
clearInterval(this.marginTimer);
this.hasMargin = true;
if (!this.hasFrameless && !this.forceFocus)
this.executeDwm(PARAMS.MARGIN, 0);
else
this.marginTimer = setInterval(() => {
try {
if (this.hasFrameless)
this.executeDwm(PARAMS.MARGIN, 0);
if (this.forceFocus)
this.executeDwm(PARAMS.FRAME, 5);
} catch (e) {
clearInterval(this.marginTimer);
this.marginTimer = null;
}
}, 1);
}
/**
* Focus on the window all the time so as not to lose the mica effect (decrease performance)
* @param {Boolean} enable
*/
alwaysFocused(enable) {
this.forceFocus = enable;
if (this.hasMargin)
this.enableMargin();
}
/**
* Apply MicaEffect (only windows 11)
*/
setMicaEffect() {
this.disableUser32();
this.enableMargin();
this.executeDwm(PARAMS.BACKGROUND.MICA, this.theme);
}
/**
* Apply MicaTabbed Effect (only windows 11)
*/
setMicaTabbedEffect() {
this.disableUser32();
this.enableMargin();
this.executeDwm(PARAMS.BACKGROUND.TABBED_MICA, this.theme);
}
/**
* Apply Acrylic Effect (only windows 11)
*/
setMicaAcrylicEffect() {
this.disableUser32();
this.enableMargin();
this.executeDwm(PARAMS.BACKGROUND.ACRYLIC, this.theme);
}
/**
* Apply dark theme to the electron app
*/
setDarkTheme() {
electron.nativeTheme.themeSource = 'dark';
if (WINDOWS_11)
this.executeDwm(this.effect, VALUE.THEME.DARK);
}
/**
* Apply light theme to the electron app
*/
setLightTheme() {
electron.nativeTheme.themeSource = 'light';
if (WINDOWS_11)
this.executeDwm(this.effect, VALUE.THEME.LIGHT);
}
/**
* Apply auto detection theme to the electron app
*/
setAutoTheme() {
electron.nativeTheme.themeSource = 'system';
if (WINDOWS_11)
this.executeDwm(this.effect, VALUE.THEME.AUTO);
}
/**
* Apply rounded corner to the electron app
*/
setRoundedCorner() {
this.executeDwm(PARAMS.CORNER, VALUE.CORNER.ROUND);
}
/**
* Apply rounded corner to the electron app
*/
setSmallRoundedCorner() {
this.executeDwm(PARAMS.CORNER, VALUE.CORNER.ROUNDSMALL);
}
/**
* Apply square corner to the electron app
*/
setSquareCorner() {
this.executeDwm(PARAMS.CORNER, VALUE.CORNER.DONOTROUND);
}
/**
* Set border color to the electron app
* @param {String} color HTML color (#RRGGBB, #RGB, or rgb(r, g, b))
*/
setBorderColor(color) {
if (color != null) {
color = getColorByString(color);
this.executeDwm(PARAMS.BORDER_COLOR, color);
}
else
this.executeDwm(PARAMS.BORDER_COLOR, 0xFFFFFFFF);
}
/**
* Set caption color to the electron app
* @param {String} color HTML color (#RRGGBB, #RGB, or rgb(r, g, b))
*/
setCaptionColor(color) {
if (color != null) {
color = getColorByString(color);
this.executeDwm(PARAMS.CAPTION_COLOR, color);
}
else
this.executeDwm(PARAMS.CAPTION_COLOR, 0xFFFFFFFF);
}
/**
* Set title text color to the electron app
* @param {String} color HTML color (#RRGGBB, #RGB, or rgb(r, g, b))
*/
setTitleTextColor(color) {
if (color != null) {
color = getColorByString(color);
this.executeDwm(PARAMS.TEXT_COLOR, color);
}
else
this.executeDwm(PARAMS.TEXT_COLOR, 0xFFFFFFFF);
}
/**
* Apply transparent Effect (windows 7+)
*/
setTransparent() {
if (WINDOWS_11)
this.disableDWM();
this.executeUser32(WIN10.TRANSPARENT, 0x00ffffff);
}
/**
* Apply blur Effect (windows 7+)
*/
setBlur() {
if (WINDOWS_11)
this.disableDWM();
this.executeUser32(WIN10.BLURBEHIND, 0x00ffffff);
}
/**
* Apply Acrylic Effect (windows 7+)
*/
setAcrylic() {
if (WINDOWS_11)
this.disableDWM();
this.executeUser32(WIN10.ACRYLIC, 0x00909090);
}
/**
* Apply custom effect of SetWindowCompositionAttribute (windows 7+)
* @param {Number} nAccentState
* @param {String} color HTML color
* @param {Number} a Alpha intensity (0 < a < 1)
*/
setCustomEffect(nAccentState, color, a) {
if (WINDOWS_11)
this.disableDWM();
a = Math.min(Math.max(Math.round(a * 255), 0), 255);
const colorToInt = getColorByString(color);
this.executeUser32(nAccentState, (a << 24) + colorToInt);
}
/**
* Disable windows 10 effect
*/
disableUser32() {
if (!this.useDWM)
this.executeUser32(0, 0xffffffff);
this.useDWM = true;
}
/**
* Disable windows 11 effect
*/
disableDWM() {
if (this.useDWM)
this.executeDwm(PARAMS.BACKGROUND.NONE, this.theme);
this.disableMargin();
this.useDWM = false;
}
/**
* Execute function 'DwmSetWindowAttribute' from dwmapi.dll
* @param {Number} params ID of the dwAttribute
* @param {Number} value New value for the params
*/
executeDwm(params, value) {
if (executeDwm) {
const HWND = this.getNativeWindowHandle()["readInt32LE"]();
executeDwm(HWND, params, value);
if (params >= 0 && params <= 4) {
this.effect = params;
this.theme = value;
}
}
}
/**
* Execute function 'SetWindowCompositionAttribute' from user32.dll
* @param {Number} params ID of the dwAttribute
* @param {Number} value New value for the params
*/
executeUser32(params, value) {
if (executeUser32) {
const HWND = this.getNativeWindowHandle()["readInt32LE"]();
executeUser32(HWND, params, value);
}
}
}
module.exports = {
PARAMS: PARAMS,
VALUE: VALUE,
MicaBrowserWindow: BrowserWindow,
IS_WINDOWS_11: WINDOWS_11,
WIN10: WIN10
};
================================================
FILE: module/app.cpp
================================================
/*
Copyright 2024 GregVido
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma warning(disable : 4996) // GetVersion() was declared deprecated
#pragma warning(disable : 4312) // long -> HWND size >
#include
#include "assets/win.cpp"
#include "assets/winstyle.h"
#include "assets/dwm.cpp"
#include "assets/user32.cpp"
#include "assets/types.h"
namespace micaElectron
{
napi_value executeDwm(napi_env env, napi_callback_info args)
{
size_t argc;
napi_get_cb_info(env, args, &argc, nullptr, nullptr, nullptr);
napi_value *argv = new napi_value[argc];
napi_get_cb_info(env, args, &argc, argv, nullptr, nullptr);
if (argc < 1)
napi_throw_error(env, nullptr, "HWND argument missing.");
else if (argc < 2)
napi_throw_error(env, nullptr, "PARAMS argument missing.");
else if (argc < 3)
napi_throw_error(env, nullptr, "VALUE argument missing.");
else
{
napi_valuetype HWNDType;
napi_valuetype ParamsType;
napi_valuetype ValueType;
napi_typeof(env, argv[0], &HWNDType);
napi_typeof(env, argv[1], &ParamsType);
napi_typeof(env, argv[2], &ValueType);
bool isHWNDNumber = HWNDType == napi_number;
bool isParamsNumber = ParamsType == napi_number;
bool isValueNumber = ValueType == napi_number;
if (!isHWNDNumber)
napi_throw_error(env, nullptr, "HWND argument must be an integer.");
else if (!isParamsNumber)
napi_throw_error(env, nullptr, "PARAMS argument must be an integer.");
else if (!isValueNumber)
napi_throw_error(env, nullptr, "VALUE argument must be an integer.");
else
{
int64_t hwnd64;
int32_t params32;
int32_t value32;
napi_get_value_int64(env, argv[0], &hwnd64);
napi_get_value_int32(env, argv[1], ¶ms32);
napi_get_value_int32(env, argv[2], &value32);
HWND hwnd = (HWND)hwnd64;
int params = (int)params32;
int value = (int)value32;
if (params == WINDOW_EDIT)
{
switch (value)
{
case RESET_BORDER:
resetBorderApp(hwnd);
break;
case ENABLE_MAXIMIZE:
enableMaximizeBox(hwnd);
break;
case INTERCEPT_MSG:
interceptMessage(hwnd);
break;
case ENABLE_CAPTION:
disableCaption(hwnd);
break;
case DISABLE_CAPTION:
enableCaption(hwnd);
break;
case FOCUS_WINDOW:
SetFocus(hwnd);
break;
case 10:
SetWindowLong(hwnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
SetWindowPos(hwnd, NULL, 0, 0, 1920, 1080, 0x0020);
break;
default:
break;
}
}
else if (!isWin11())
napi_throw_error(env, nullptr, "Mica-Electron work only on Windows 11.");
else
{
enableDWM();
if (params <= MICA_EFFECT)
{
setAppTheme(value, hwnd);
bool success = applyMicaEffect(params, hwnd);
if (!success)
{
napi_throw_error(env, nullptr, "You use old version of windows 11, you have don't have ACRYLIC and MICA_TABBED.");
return nullptr;
}
}
else
{
switch (params)
{
case CORNER_TYPE:
setCorner(value, hwnd);
break;
case BORDER_COLOR:
setBorderColor(value, hwnd);
break;
case CAPTION_COLOR:
setCaptionColor(value, hwnd);
break;
case TEXT_COLOR:
setTextColor(value, hwnd);
break;
case MARGIN_TYPE:
setMargin(value, hwnd);
break;
default:
break;
}
}
disableDWM();
}
}
}
return nullptr;
}
napi_value executeUser32(napi_env env, napi_callback_info args)
{
size_t argc;
napi_get_cb_info(env, args, &argc, nullptr, nullptr, nullptr);
napi_value *argv = new napi_value[argc];
napi_get_cb_info(env, args, &argc, argv, nullptr, nullptr);
if (argc < 1)
napi_throw_error(env, nullptr, "HWND argument missing.");
else if (argc < 2)
napi_throw_error(env, nullptr, "PARAMS argument missing.");
else if (argc < 3)
napi_throw_error(env, nullptr, "VALUE argument missing.");
else
{
napi_valuetype HWNDType;
napi_valuetype ParamsType;
napi_valuetype ValueType;
napi_typeof(env, argv[0], &HWNDType);
napi_typeof(env, argv[1], &ParamsType);
napi_typeof(env, argv[2], &ValueType);
bool isHWNDNumber = HWNDType == napi_number;
bool isParamsNumber = ParamsType == napi_number;
bool isValueNumber = ValueType == napi_number;
if (!isHWNDNumber)
napi_throw_error(env, nullptr, "HWND argument must be an integer.");
else if (!isParamsNumber)
napi_throw_error(env, nullptr, "PARAMS argument must be an integer.");
else if (!isValueNumber)
napi_throw_error(env, nullptr, "VALUE argument must be an integer.");
else
{
int64_t hwnd64;
int32_t params32;
int32_t value32;
napi_get_value_int64(env, argv[0], &hwnd64);
napi_get_value_int32(env, argv[1], ¶ms32);
napi_get_value_int32(env, argv[2], &value32);
enableUser32();
HWND hwnd = (HWND)hwnd64;
int params = (int)params32;
int value = (int)value32;
applyWindows10Effect(params, value, hwnd);
disableUser32();
}
}
return nullptr;
}
napi_value redraw(napi_env env, napi_callback_info args)
{
size_t argc;
napi_get_cb_info(env, args, &argc, nullptr, nullptr, nullptr);
napi_value *argv = new napi_value[argc];
napi_get_cb_info(env, args, &argc, argv, nullptr, nullptr);
if (argc < 1)
napi_throw_error(env, nullptr, "HWND argument missing.");
else if (argc < 2)
napi_throw_error(env, nullptr, "X argument missing.");
else if (argc < 3)
napi_throw_error(env, nullptr, "Y argument missing.");
else if (argc < 4)
napi_throw_error(env, nullptr, "WIDTH argument missing.");
else if (argc < 5)
napi_throw_error(env, nullptr, "HEIGHT argument missing.");
else
{
napi_valuetype HWNDType;
napi_valuetype XType;
napi_valuetype YType;
napi_valuetype WidthType;
napi_valuetype HeightType;
napi_typeof(env, argv[0], &HWNDType);
napi_typeof(env, argv[1], &XType);
napi_typeof(env, argv[2], &YType);
napi_typeof(env, argv[3], &WidthType);
napi_typeof(env, argv[4], &HeightType);
bool isHWNDNumber = HWNDType == napi_number;
bool isXNumber = XType == napi_number;
bool isYNumber = YType == napi_number;
bool isWidthNumber = WidthType == napi_number;
bool isHeightNumber = HeightType == napi_number;
if (!isHWNDNumber)
napi_throw_error(env, nullptr, "HWND argument must be an integer.");
else if (!isXNumber)
napi_throw_error(env, nullptr, "X argument must be an integer.");
else if (!isYNumber)
napi_throw_error(env, nullptr, "Y argument must be an integer.");
else if (!isWidthNumber)
napi_throw_error(env, nullptr, "WIDTH argument must be an integer.");
else if (!isHeightNumber)
napi_throw_error(env, nullptr, "HEIGHT argument must be an integer.");
else
{
int64_t hwnd64;
int32_t x32;
int32_t y32;
int32_t width32;
int32_t height32;
napi_get_value_int64(env, argv[0], &hwnd64);
napi_get_value_int32(env, argv[1], &x32);
napi_get_value_int32(env, argv[2], &y32);
napi_get_value_int32(env, argv[3], &width32);
napi_get_value_int32(env, argv[4], &height32);
HWND hwnd = (HWND)hwnd64;
int x = (int)x32;
int y = (int)y32;
int width = (int)width32;
int height = (int)height32;
SetWindowPos(hwnd, 0, x, y, width, height, 0x0020);
}
}
return nullptr;
}
napi_value init(napi_env env, napi_value exports)
{
napi_status status;
napi_value fn1;
napi_value fn2;
napi_value fn3;
status = napi_create_function(env, nullptr, 0, executeDwm, nullptr, &fn1);
if (status != napi_ok)
return nullptr;
status = napi_set_named_property(env, exports, "executeDwm", fn1);
if (status != napi_ok)
return nullptr;
status = napi_create_function(env, nullptr, 0, executeUser32, nullptr, &fn2);
if (status != napi_ok)
return nullptr;
status = napi_set_named_property(env, exports, "executeUser32", fn2);
if (status != napi_ok)
return nullptr;
status = napi_create_function(env, nullptr, 0, redraw, nullptr, &fn3);
if (status != napi_ok)
return nullptr;
status = napi_set_named_property(env, exports, "redraw", fn3);
if (status != napi_ok)
return nullptr;
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
}
================================================
FILE: module/assets/dwm.cpp
================================================
/*
Copyright 2024 GregVido
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "dwm.h"
#include "winstyle.h"
#include
HINSTANCE dwmapi;
pDwmSetWindowAttribute SetWindowAttribute;
pDwmExtendFrameIntoClientArea ExtendFrameIntoClientArea;
bool dwmEnabled = false;
void enableDWM()
{
dwmapi = LoadLibrary(TEXT("dwmapi.dll"));
SetWindowAttribute = (pDwmSetWindowAttribute)GetProcAddress(dwmapi, "DwmSetWindowAttribute");
ExtendFrameIntoClientArea = (pDwmExtendFrameIntoClientArea)GetProcAddress(dwmapi, "DwmExtendFrameIntoClientArea");
dwmEnabled = true;
}
void disableDWM()
{
FreeLibrary(dwmapi);
dwmEnabled = false;
}
void setAppTheme(int value, HWND hwnd)
{
if (dwmEnabled)
{
int useDarkTheme = 0x00;
// if dark mod, apply dark effect
if (value == 1 /* DARK */ || (value == 5 /* AUTO */ && !is_light_theme()))
useDarkTheme = 0x01;
SetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkTheme, sizeof(int));
}
}
bool applyMicaEffect(int type, HWND hwnd)
{
if(dwmEnabled) {
bool insider = isInsider();
if (insider)
SetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &type, sizeof(int));
else if (type > 2)
return false;
else
{
int micaEnable = 0x00;
if (type == 1)
micaEnable = 0x02;
else if (type == 2)
micaEnable = 0x01;
SetWindowAttribute(hwnd, DWMWA_MICA_EFFECT, &micaEnable, sizeof(int));
}
}
return true;
}
void setCorner(int type, HWND hwnd) {
if(dwmEnabled)
SetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &type, sizeof(int));
}
void setBorderColor(int type, HWND hwnd) {
if(dwmEnabled)
SetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &type, sizeof(int));
}
void setCaptionColor(int type, HWND hwnd) {
if(dwmEnabled)
SetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &type, sizeof(int));
}
void setTextColor(int type, HWND hwnd) {
if(dwmEnabled)
SetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, &type, sizeof(int));
}
void setMargin(int type, HWND hwnd) {
if(dwmEnabled) {
MARGINS margins;
if(type == 0)
margins = {-1};
else
margins = {};
ExtendFrameIntoClientArea(hwnd, &margins);
}
}
================================================
FILE: module/assets/dwm.h
================================================
#ifndef DWM_H
#define DWM_H
#define DWMWA_MICA_EFFECT DWORD(1029)
#define DWMWA_SYSTEMBACKDROP_TYPE DWORD(38)
#define DWMWA_USE_IMMERSIVE_DARK_MODE DWORD(20)
#define DWMWA_WINDOW_CORNER_PREFERENCE DWORD(33)
#define DWMWA_BORDER_COLOR DWORD(34)
#define DWMWA_CAPTION_COLOR DWORD(35)
#define DWMWA_TEXT_COLOR DWORD(36)
#endif
================================================
FILE: module/assets/types.h
================================================
#ifndef TYPE_H
#define TYPE_H
#define MICA_EFFECT 4
#define CORNER_TYPE 5
#define BORDER_COLOR 6
#define CAPTION_COLOR 7
#define TEXT_COLOR 8
#define WINDOW_EDIT 9
#define RESET_BORDER 0
#define ENABLE_MAXIMIZE 1
#define INTERCEPT_MSG 2
#define ENABLE_CAPTION 3
#define DISABLE_CAPTION 4
#define FOCUS_WINDOW 5
#define MARGIN_TYPE 10
#endif
================================================
FILE: module/assets/user32.cpp
================================================
/*
Copyright 2024 GregVido
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "winstyle.h"
HINSTANCE user32;
pSetWindowCompositionAttribute SetWindowCompositionAttributeUser32;
bool user32Enabled = false;
void enableUser32()
{
user32 = LoadLibrary(TEXT("user32.dll"));
SetWindowCompositionAttributeUser32 = (pSetWindowCompositionAttribute)GetProcAddress(user32, "SetWindowCompositionAttribute");
user32Enabled = true;
}
void disableUser32()
{
FreeLibrary(user32);
user32Enabled = false;
}
void resetBorderApp(HWND hwnd)
{
SetWindowLongA(hwnd, -16, 0x004F0000L);
}
void enableMaximizeBox(HWND hwnd)
{
LONG_PTR style = GetWindowLongA(hwnd, GWL_STYLE);
style |= WS_SIZEBOX;
style |= WS_THICKFRAME;
style |= WS_MAXIMIZEBOX;
SetWindowLongA(hwnd, GWL_STYLE, style);
}
void interceptMessage(HWND hwnd)
{
originalWndProc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WindowProc);
}
void disableCaption(HWND hwnd)
{
LONG_PTR style = GetWindowLongA(hwnd, GWL_STYLE);
style &= ~WS_CAPTION;
SetWindowLongA(hwnd, GWL_STYLE, style);
}
void enableCaption(HWND hwnd)
{
LONG_PTR style = GetWindowLongA(hwnd, GWL_STYLE);
style |= WS_CAPTION;
SetWindowLongA(hwnd, GWL_STYLE, style);
}
void applyWindows10Effect(int nAccentState, int nColor, HWND hwnd)
{
if (user32Enabled)
{
ACCENTPOLICY policy;
policy.nAccentState = nAccentState;
policy.nFlags = 2;
policy.nColor = nColor;
policy.nAnimationId = 0;
WINCOMATTRPDATA data;
data.nAttribute = 19;
data.pData = &policy;
data.ulDataSize = sizeof(policy);
SetWindowCompositionAttributeUser32(hwnd, &data);
}
}
================================================
FILE: module/assets/win.cpp
================================================
/*
Copyright 2024 GregVido
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include
#include
#include
#include
WNDPROC originalWndProc;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SYSCOMMAND:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_SIZE:
/*
if (wParam == SIZE_MAXIMIZED)
return DefWindowProc(hwnd, uMsg, wParam, lParam);*/
return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam);
default:
return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam);
}
}
DWORD getBuild()
{
DWORD dwVersion = 0;
DWORD dwMajorVersion = 0;
DWORD dwMinorVersion = 0;
DWORD dwBuild = 0;
dwVersion = GetVersion();
dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
if (dwVersion < 0x80000000)
dwBuild = (DWORD)(HIWORD(dwVersion));
return dwBuild;
}
bool isInsider()
{
return getBuild() >= 22621;
}
bool isWin11()
{
return getBuild() >= 22000;
}
bool is_light_theme()
{
// based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application
// The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian
auto buffer = std::vector(4);
auto cbData = static_cast(buffer.size() * sizeof(char));
auto res = RegGetValueW(
HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
L"AppsUseLightTheme",
RRF_RT_REG_DWORD, // expected value type
nullptr,
buffer.data(),
&cbData);
if (res != ERROR_SUCCESS)
{
throw std::runtime_error("Error: error_code=" + std::to_string(res));
}
// convert bytes written to our buffer to an int, assuming little-endian
auto i = int(buffer[3] << 24 |
buffer[2] << 16 |
buffer[1] << 8 |
buffer[0]);
return i == 1;
}
================================================
FILE: module/assets/winstyle.h
================================================
#include
#ifndef WIN_STYLE_H
#define WIN_STYLE_H
struct ACCENTPOLICY
{
int nAccentState;
int nFlags;
int nColor;
int nAnimationId;
};
struct WINCOMATTRPDATA
{
int nAttribute;
PVOID pData;
ULONG ulDataSize;
};
typedef BOOL(WINAPI *pSetWindowCompositionAttribute)(HWND, WINCOMATTRPDATA *);
typedef BOOL(WINAPI *pDwmSetWindowAttribute)(HWND, DWORD, int *, int);
typedef BOOL(WINAPI *pSetLayeredWindowAttributes)(HWND, int, byte, int);
typedef BOOL(WINAPI *pDwmExtendFrameIntoClientArea)(HWND, MARGINS *);
typedef BOOL(WINAPI *pSetWindowPos)(HWND, HWND, int, int, int, int, int);
typedef HWND(WINAPI *pSetWindowLongA)(HWND, int, long);
typedef LONG(WINAPI *pGetWindowLongA)(HWND, int);
#endif
================================================
FILE: package.json
================================================
{
"name": "mica-electron",
"version": "1.5.17",
"description": "Tool to add mica effect of windows 11 in electron app",
"main": "main.js",
"types": "./types/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/GregVido/mica-electron.git"
},
"keywords": [
"electron",
"mica",
"windows 11",
"acrylic",
"transparent",
"glasstron"
],
"author": "GregVido",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GregVido/mica-electron/issues"
},
"homepage": "https://github.com/GregVido/mica-electron",
"devDependencies": {
"electron": "^33.2.1",
"@electron/rebuild": "^3.7.1"
}
}
================================================
FILE: types/index.d.ts
================================================
import "electron";
declare module "mica-electron" {
// TODO: rework CSS colors
// type HexColor = `#${string}`;
// type RGBColor = `rgb(${number}, ${number}, ${number})`;
// type CSSColor = string;
export class MicaBrowserWindow extends Electron.BrowserWindow {
/**
* Current theme value, represented by an integer
*/
public theme: number;
/**
* Current effect value, represented by an integer
*/
public effect: number;
/**
* Shows whether this window is using DWM or User32
*/
public useDWM: boolean;
/**
* Shows whether this window is frameless
*/
public hasFrameless: boolean;
/**
* Disable transparent for mica effect
*/
public disableMargin(): void;
/**
* Enable transparent for mica effect
*/
public enableMargin(): void;
/**
* Apply the Mica effect (Windows 11 only)
*/
public setMicaEffect(): void;
/**
* Apply the Tabbed Mica effect (Windows 11 only)
*/
public setMicaTabbedEffect(): void
/**
* Apply the Acrylic effect (Windows 11 only)
*/
public setMicaAcrylicEffect(): void;
/**
* Change theme to dark
*/
public setDarkTheme(): void;
/**
* Change theme to light
*/
public setLightTheme(): void;
/**
* Make theme automatically adapt to the system
*/
public setAutoTheme(): void;
/**
* Set corner radius to normal (Windows 11 only)
*/
public setRoundedCorner(): void;
/**
* Set corner radius to small (Windows 11 only)
*/
public setSmallRoundedCorner(): void;
/**
* Set corner radius to 0 (Windows 11 only)
*/
public setSquareCorner(): void;
/**
* Change window's border color
*/
public setBorderColor(color: string): void;
/**
* Change window's titlebar color
*/
public setCaptionColor(color: string): void;
/**
* Change window's title text color
*/
public setTitleTextColor(color: string): void;
/**
* Apply the Transparent Effect (Windows 7+)
*/
public setTransparent(): void;
/**
* Apply the Blur Effect (Windows 7+)
*/
public setBlur(): void;
/**
* Apply the Acrylic Effect (Windows 7+)
*/
public setAcrylic(): void;
/**
* Apply a custom effect of SetWindowCompositionAttribute (Windows 7+)
*/
public setCustomEffect(nAccentState: number, color: string, alpha: number): void;
/**
* Disables support for Windows 10 effects (Acrylic and Blur)
*/
public disableUser32(): void;
/**
* Disables support for Windows 11 effects (Mica and Acrylic)
*/
public disableDWM(): void;
/**
* Executes the `DwmSetWindowAttribute` function from `dwmapi.dll`
*/
public executeDwm(params: number, value: number): void;
/**
* Executes the `SetWindowCompositionAttribute` function from `user32.dll`
*/
public executeUser32(params: number, value: number): void;
}
/**
* Shows if the machine is running Windows 11
*/
export const IS_WINDOWS_11: boolean;
/**
* Values specifying several window properties
*/
export const PARAMS: {
/**
* Effects configuration
*/
BACKGROUND: {
AUTO: 0,
NONE: 1,
/**
* Acrylic Effect
*/
ACRYLIC: 3,
/**
* Mica Effect
*/
MICA: 2,
/**
* Mica Tabbed Effect
*/
TABBED_MICA: 4
},
/**
* Corner radius configuration
*/
CORNER: 5,
/**
* Border color configuration
*/
BORDER_COLOR: 6,
/**
* Titlebar color configuration
*/
CAPTION_COLOR: 7,
/**
* Text color configuration
*/
TEXT_COLOR: 8,
FRAME: 9,
MARGIN: 10
};
/**
* Values which can be used in several effects
*/
export const VALUE: {
/**
* Theme values
*/
THEME: {
/**
* Automatic theme
*/
AUTO: 5,
/**
* Dark theme
*/
DARK: 1,
/**
* Light theme
*/
LIGHT: 2,
},
/**
* Corner radius values
*/
CORNER: {
/**
* Default corners
*/
DEFAULT: 0,
/**
* Square corners
*/
DONOTROUND: 1,
/**
* Rounded corners
*/
ROUND: 2,
/**
* Small rounded corners
*/
ROUNDSMALL: 3
},
/**
* Color values
*/
COLOR: {
RED: 0x000000FF,
GREEN: 0x0000FF00,
BLUE: 0x00FF0000,
BLACK: 0x00000000,
WHITE: 0x00FFFFFF,
/**
* Convert red, green and blue values into a single hex color
*/
FROM_RGB: (red: number, green: number, blue: number) => number
},
/**
* Falsely value
*/
FALSE: 0,
/**
* Truthful value
*/
TRUE: 1
};
/**
* Windows 10 effects
*/
export const WIN10: {
/**
* Transparent Effect
*/
TRANSPARENT: 2,
/**
* Blur Effect
*/
BLURBEHIND: 3,
/**
* Acrylic Effect
*/
ACRYLIC: 4
}
}