最近開始學習如何使用 Pencil 與 AI 互動進行畫面的設計,但我開發環境喜歡在 WSL2,不過我真的很不喜歡在 VS CODE 中使用 Pencil,所以我是用自行安裝 appImage 的方式,直接跑起 WSL2 的 GUI。
有個比較大的問題是,WSL2 的下的 pencil 不會跟著 Windows 11 的縮放比例進行調整,經查詢 pencil 是基於 electron 技術開發的,而 electron 又是 chromium ,所以調整甚麼環境變數沒有用,經過跟 AI 查詢後,electron 的應用可以透過 --force-device-scale-factor=1.25 這樣的方式調整縮放比為 125%,經過實驗確定是可以的。
可是還有一個問題,我其實是使用筆電,外出時是使用筆電的螢幕,縮放比是 150%,而在家則是外接螢幕,縮放比是 125%,但 WSL2 預設會直接用實際解析度,字都超小那怎麼辦呢 ?
在 Github 這則討論 Set dpi of wsl2 windows independently of screen resolution 有一位 A-Ribeiro 有撰寫了一個 sh , 可以抓取 Windows 11 的縮放比的方式自動設定環境變數,但我先前說過環境變數設定無效,但抓縮放比的方式卻能拿來用,於是又請了 AI 大神直接改造,做到執行 pencil 時,自動抓取 Windows 11 的設定,達成外出在家都得到一致性的縮放比例。
以下是原始碼
#!/bin/bash
# ──────────────────────────────────────────────
# 透過 PowerShell 讀取 Windows 主螢幕 DPI scale
# ──────────────────────────────────────────────
powershell_script='
Add-Type @"
using System.Runtime.InteropServices;
using System;
using System.Globalization;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEVMODEA
{
private const int CCHDEVICENAME = 32;
private const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public int dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
public class DCOps {
[DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")] public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll")] public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public const int LOGPIXELSX = 88;
public const int LOGPIXELSY = 90;
[DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess();
[DllImport("shcore.dll")] static extern int GetProcessDpiAwareness(IntPtr hprocess, out int lpdpiAwareness);
[DllImport("shcore.dll")] static extern int SetProcessDpiAwareness(int value);
public const int PROCESS_DPI_UNAWARE = 0;
public const int PROCESS_SYSTEM_DPI_AWARE = 1;
public const int PROCESS_PER_MONITOR_DPI_AWARE = 2;
[DllImport("Shcore.dll")]
static extern int GetDpiForMonitor(IntPtr hmonitor, uint dpiType, out uint dpiX, out uint dpiY);
public const uint MDT_EFFECTIVE_DPI = 0;
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
public const uint MONITOR_DEFAULTTOPRIMARY = 1;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct RECT { public int left, top, right, bottom; }
[DllImport("user32.dll")] static extern IntPtr GetDesktopWindow();
public static void run() {
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
var hwnd = GetDesktopWindow();
var hdc = GetDC(hwnd);
if (hdc == IntPtr.Zero) { Console.WriteLine("1.25"); return; }
var hmonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
if (hmonitor == IntPtr.Zero) { ReleaseDC(hwnd, hdc); Console.WriteLine("1.25"); return; }
double scaleFactor = 1.0;
int processDpiAwareness;
if (GetProcessDpiAwareness(GetCurrentProcess(), out processDpiAwareness) >= 0) {
if (processDpiAwareness == PROCESS_PER_MONITOR_DPI_AWARE) {
uint dpiX, dpiY;
if (GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, out dpiX, out dpiY) >= 0) {
scaleFactor = Math.Round(((double)(dpiX + dpiY) * 0.5) / 96.0, 2);
}
} else {
int dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
int dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
scaleFactor = Math.Round(((double)(dpiX + dpiY) * 0.5) / 96.0, 2);
}
}
ReleaseDC(hwnd, hdc);
Console.WriteLine(String.Format(CultureInfo.InvariantCulture, "{0}", scaleFactor));
}
}
"@
[DCOps]::run()
'
# ──────────────────────────────────────────────
# 取得 Windows DPI scale factor
# ──────────────────────────────────────────────
SCALE=$("/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe" \
-NoProfile -NonInteractive -Command "$powershell_script" 2>/dev/null \
| tr -d '\r\n')
# 若偵測失敗,fallback 到 1.0
if [[ -z "$SCALE" || "$SCALE" == "0" ]]; then
SCALE="1.0"
echo "[pencil] DPI 偵測失敗,使用預設值 ${SCALE}"
else
echo "[pencil] 偵測到 Windows DPI scale: ${SCALE}"
fi
# ──────────────────────────────────────────────
# 啟動 Pencil(Electron app 使用 Chromium flag)
# ──────────────────────────────────────────────
exec ~/Applications/Pencil-linux-x86_64.AppImage \
--force-device-scale-factor="${SCALE}" \
"$@"
這段原始碼是符合我的環境如下:
- 我將 pencil 放置於
~/Applications/Pencil-linux-x86_64.AppImage, 權限必須有+x。 - 我將 bash 存放於
~/.local/bin/pencil, 權限必須有+x。
使用時就是直接執行 pencil 就可以了,以下對照圖左邊是 Windows 11 , 右邊是 WSL2,文字與 UI 大小完全一樣。



