Skip to content

Commit fafbde9

Browse files
authored
Support DPI change in candidate/infolist window (google#1481)
This is part of our ongoing effort to fully conform Per-Monitor DPI v2 protocol in Windows (google#831). With this commit, both the candidate window and the infolist window become fully aware of dynamic DPI changes. The indicator window and icons managed by text input processor (TIP) DLLs remain unchanged. The key implementation idea is to not rely on WM_DPICHANGED message [1]. It is really tempting to implement WM_DPICHANGED handler, but it turns out to be redundant for our use cases. Consider the following two cases explained in the WM_DPICHANGED message document [1]. 1. When the candidate/infolist window is moving into the monitor with a different DPI. In this case, win32::WindowManager should be aware of which monitor the candidate/infolist window will be displayed on, and can let the candidate/infolist window know the new DPI without relying on the WM_DPICHANGED message. 2. When the DPI of the current monitor changes while the candidate/infolist window is visible. In this case, the target application window typically re-renders to adapt to the new DPI, which then triggers a fresh layout pass on our candidate/infolist window. As explained above, win32::WindowManager always checks the DPI of the target monitor without relying on the WM_DPICHANGED message. Both cases arise because the candidate and infolist windows behave like popups anchored to the cursor location in the target application window; WM_DPICHANGED is primarily meaningful for top-level windows that users can drag and move across the monitors. Other than that, the implementation is rather straightforward: we just need to make sure to keep propagating the DPI information to the text renderer and to update the DPI-dependent resources when the DPI changes. Closes google#1459. [1]: https://learn.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged PiperOrigin-RevId: 906746593
1 parent 46058f5 commit fafbde9

13 files changed

Lines changed: 159 additions & 76 deletions

src/bazel/win32/BUILD.bazel

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ mozc_win32_lib(
9797
name = "pathcch",
9898
)
9999

100+
mozc_win32_lib(
101+
name = "shcore",
102+
)
103+
100104
mozc_win32_lib(
101105
name = "user32",
102106
)

src/renderer/win32/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ mozc_cc_library(
107107
tags = MOZC_TAGS.WIN_ONLY,
108108
target_compatible_with = ["@platforms//os:windows"],
109109
deps = [
110+
"//bazel/win32:shcore",
110111
"//bazel/win32:user32",
111112
"//protocol:renderer_cc_proto",
112113
"//renderer:renderer_style_handler",
@@ -122,6 +123,7 @@ mozc_cc_library(
122123
deps = [
123124
":resource_h",
124125
":text_renderer",
126+
":win32_dpi_util",
125127
"//base:const",
126128
"//base:coordinates",
127129
"//base/win32:wide_char",
@@ -251,6 +253,7 @@ mozc_cc_library(
251253
":candidate_window",
252254
":indicator_window",
253255
":infolist_window",
256+
":win32_dpi_util",
254257
":win32_renderer_util",
255258
"//base:coordinates",
256259
"//client:client_interface",

src/renderer/win32/candidate_window.cc

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <wil/resource.h>
3636
#include <windows.h>
3737

38+
#include <cstdint>
3839
#include <memory>
3940
#include <sstream>
4041
#include <string>
@@ -57,9 +58,6 @@ namespace renderer {
5758
namespace win32 {
5859
namespace {
5960

60-
// 96 DPI is the default DPI in Windows.
61-
constexpr int kDefaultDPI = 96;
62-
6361
// layout size constants in pixel unit in the default DPI.
6462
constexpr int kIndicatorWidthInDefaultDPI = 4;
6563

@@ -239,12 +237,19 @@ CandidateWindow::CandidateWindow()
239237
footer_logo_display_size_(0, 0),
240238
send_command_interface_(nullptr),
241239
table_layout_(std::make_unique<TableLayout>()),
242-
text_renderer_(TextRenderer::Create()),
240+
dpi_(::GetDpiForSystem()),
241+
text_renderer_(TextRenderer::Create(dpi_)),
243242
indicator_width_(0),
244243
metrics_changed_(false),
245244
mouse_moving_(true) {
245+
UpdateDpiDependentResources();
246+
}
247+
248+
CandidateWindow::~CandidateWindow() = default;
249+
250+
void CandidateWindow::UpdateDpiDependentResources() {
251+
const double scale_factor = GetDPIScalingFactor(dpi_);
246252
double image_scale_factor = 1.0;
247-
const double scale_factor = GetDPIScalingFactor();
248253
if (scale_factor < 1.125) {
249254
footer_logo_.reset(LoadBitmapFromResource(::GetModuleHandle(nullptr),
250255
IDB_FOOTER_LOGO_COLOR_100));
@@ -263,7 +268,7 @@ CandidateWindow::CandidateWindow()
263268
image_scale_factor = 2.0;
264269
}
265270

266-
// If DPI is not default value, re-calculate the size based on the DPI.
271+
footer_logo_display_size_ = Size(0, 0);
267272
if (footer_logo_.is_valid()) {
268273
BITMAP bm = {};
269274
if (::GetObject(footer_logo_.get(), sizeof(bm), &bm)) {
@@ -276,13 +281,20 @@ CandidateWindow::CandidateWindow()
276281
indicator_width_ = kIndicatorWidthInDefaultDPI * scale_factor;
277282
}
278283

279-
CandidateWindow::~CandidateWindow() {}
280-
281284
LRESULT CandidateWindow::OnCreate(LPCREATESTRUCT create_struct) {
282285
EnableOrDisableWindowForWorkaround();
283286
return 0;
284287
}
285288

289+
void CandidateWindow::UpdateDpi(uint32_t dpi) {
290+
if (dpi == dpi_) {
291+
return;
292+
}
293+
dpi_ = dpi;
294+
UpdateDpiDependentResources();
295+
text_renderer_->OnDpiChanged(dpi_);
296+
}
297+
286298
void CandidateWindow::EnableOrDisableWindowForWorkaround() {
287299
// Disable the window if SPI_GETACTIVEWINDOWTRACKING is enabled.
288300
// See b/2317702 for details.
@@ -302,10 +314,6 @@ void CandidateWindow::OnDestroy() {
302314
::PostQuitMessage(0);
303315
}
304316

305-
void CandidateWindow::OnDpiChanged(UINT dpiX, UINT dpiY, RECT* rect) {
306-
metrics_changed_ = true;
307-
}
308-
309317
BOOL CandidateWindow::OnEraseBkgnd(HDC dc) {
310318
// We do not have to erase background
311319
// because all pixels in client area will be drawn in the DoPaint method.
@@ -839,7 +847,7 @@ void CandidateWindow::DrawSelectedRect(HDC dc) {
839847

840848
void CandidateWindow::DrawInformationIcon(HDC dc) {
841849
DCHECK(table_layout_->IsLayoutFrozen()) << "Table layout is not frozen.";
842-
const double scale_factor = GetDPIScalingFactor();
850+
const double scale_factor = GetDPIScalingFactor(dpi_);
843851
for (size_t i = 0; i < candidate_window_->candidate_size(); ++i) {
844852
if (candidate_window_->candidate(i).has_information_id()) {
845853
CRect rect = ToCRect(table_layout_->GetRowRect(i));

src/renderer/win32/candidate_window.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <wil/resource.h>
3737
#include <windows.h>
3838

39+
#include <cstdint>
3940
#include <memory>
4041

4142
#include "base/const.h"
@@ -72,7 +73,6 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
7273
BEGIN_MSG_MAP(CandidateWindow)
7374
MESSAGE_HANDLER(WM_CREATE, OnCreate)
7475
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
75-
MESSAGE_HANDLER(WM_DPICHANGED, OnDpiChanged)
7676
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
7777
MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
7878
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
@@ -89,7 +89,6 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
8989
~CandidateWindow();
9090
LRESULT OnCreate(LPCREATESTRUCT create_struct);
9191
void OnDestroy();
92-
void OnDpiChanged(UINT dpiX, UINT dpiY, RECT* rect);
9392
BOOL OnEraseBkgnd(HDC dc);
9493
void OnGetMinMaxInfo(MINMAXINFO* min_max_info);
9594
void OnLButtonDown(UINT nFlags, CPoint point);
@@ -101,6 +100,12 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
101100

102101
void set_mouse_moving(bool moving);
103102

103+
// If |dpi| differs from the cached DPI, updates the cached DPI, refreshes
104+
// DPI-dependent resources, and flags the text renderer for refresh on the
105+
// next UpdateLayout. Idempotent — safe to call unconditionally before
106+
// UpdateLayout.
107+
void UpdateDpi(uint32_t dpi);
108+
104109
void UpdateLayout(const commands::CandidateWindow& candidate_window);
105110
void SetSendCommandInterface(
106111
client::SendCommandInterface* send_command_interface);
@@ -123,6 +128,10 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
123128
void DrawBackground(HDC dc);
124129
void DrawFrame(HDC dc);
125130

131+
// Recomputes DPI-dependent cached resources (footer logo and indicator
132+
// width) for the current |dpi_|.
133+
void UpdateDpiDependentResources();
134+
126135
// Handles candidate selection by mouse.
127136
void HandleMouseEvent(UINT nFlags, const CPoint& point,
128137
bool close_candidatewindow);
@@ -142,13 +151,6 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
142151
OnDestroy();
143152
return 0;
144153
}
145-
inline LRESULT OnDpiChanged(UINT msg_id, WPARAM wparam, LPARAM lparam,
146-
BOOL& handled) {
147-
OnDpiChanged(static_cast<UINT>(LOWORD(wparam)),
148-
static_cast<UINT>(HIWORD(wparam)),
149-
reinterpret_cast<RECT*>(lparam));
150-
return 0;
151-
}
152154
inline LRESULT OnEraseBkgnd(UINT msg_id, WPARAM wparam, LPARAM lparam,
153155
BOOL& handled) {
154156
return static_cast<LRESULT>(OnEraseBkgnd(reinterpret_cast<HDC>(wparam)));
@@ -198,6 +200,7 @@ class CandidateWindow : public ATL::CWindowImpl<CandidateWindow, ATL::CWindow,
198200
Size footer_logo_display_size_;
199201
client::SendCommandInterface* send_command_interface_;
200202
std::unique_ptr<TableLayout> table_layout_;
203+
uint32_t dpi_;
201204
std::unique_ptr<TextRenderer> text_renderer_;
202205
int indicator_width_;
203206
bool metrics_changed_;

src/renderer/win32/indicator_window.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class IndicatorWindow::WindowImpl
251251

252252
void LoadSprite(int mode) {
253253
BalloonImage::BalloonImageInfo info;
254-
LOGFONT logfont = GetMessageBoxLogFont();
254+
LOGFONT logfont = GetMessageBoxLogFont(::GetDpiForSystem());
255255
info.label_font = mozc::win32::WideToUtf8(logfont.lfFaceName);
256256

257257
info.frame_color = RGBColor(1, 122, 204);

src/renderer/win32/infolist_window.cc

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <wil/resource.h>
3636
#include <windows.h>
3737

38+
#include <cstdint>
3839
#include <string>
3940

4041
#include "base/coordinates.h"
@@ -79,26 +80,32 @@ void FillSolidRect(HDC dc, const RECT* rect, COLORREF color) {
7980
InfolistWindow::InfolistWindow()
8081
: send_command_interface_(nullptr),
8182
candidate_window_(new commands::CandidateWindow),
82-
text_renderer_(TextRenderer::Create()),
83+
dpi_(::GetDpiForSystem()),
84+
text_renderer_(TextRenderer::Create(dpi_)),
8385
style_(new RendererStyle),
8486
metrics_changed_(false),
8587
visible_(false) {
86-
GetScaledRendererStyle(style_.get());
88+
GetScaledRendererStyle(style_.get(), dpi_);
8789
}
8890

8991
InfolistWindow::~InfolistWindow() {}
9092

93+
void InfolistWindow::UpdateDpi(uint32_t dpi) {
94+
if (dpi == dpi_) {
95+
return;
96+
}
97+
dpi_ = dpi;
98+
GetScaledRendererStyle(style_.get(), dpi_);
99+
text_renderer_->OnDpiChanged(dpi_);
100+
}
101+
91102
void InfolistWindow::OnDestroy() {
92103
// PostQuitMessage may stop the message loop even though other
93104
// windows are not closed. WindowManager should close these windows
94105
// before process termination.
95106
::PostQuitMessage(0);
96107
}
97108

98-
void InfolistWindow::OnDpiChanged(UINT dpiX, UINT dpiY, RECT* rect) {
99-
metrics_changed_ = true;
100-
}
101-
102109
BOOL InfolistWindow::OnEraseBkgnd(HDC dc) {
103110
// We do not have to erase background
104111
// because all pixels in client area will be drawn in the DoPaint method.

src/renderer/win32/infolist_window.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <atlwin.h>
3636
#include <windows.h>
3737

38+
#include <cstdint>
3839
#include <memory>
3940
#include <string>
4041

@@ -64,7 +65,6 @@ class InfolistWindow : public ATL::CWindowImpl<InfolistWindow, ATL::CWindow,
6465

6566
BEGIN_MSG_MAP(InfolistWindow)
6667
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
67-
MESSAGE_HANDLER(WM_DPICHANGED, OnDpiChanged)
6868
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
6969
MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
7070
MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
@@ -78,14 +78,19 @@ class InfolistWindow : public ATL::CWindowImpl<InfolistWindow, ATL::CWindow,
7878
InfolistWindow& operator=(const InfolistWindow&) = delete;
7979
~InfolistWindow();
8080
void OnDestroy();
81-
void OnDpiChanged(UINT dpiX, UINT dpiY, RECT* rect);
8281
BOOL OnEraseBkgnd(HDC dc);
8382
void OnGetMinMaxInfo(MINMAXINFO* min_max_info);
8483
void OnPaint(HDC dc);
8584
void OnPrintClient(HDC dc, UINT uFlags);
8685
void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);
8786
void OnTimer(UINT_PTR nIDEvent);
8887

88+
// If |dpi| differs from the cached DPI, updates the cached DPI, re-scales
89+
// |style_|, and flags the text renderer for refresh on the next
90+
// UpdateLayout. Idempotent — safe to call unconditionally before
91+
// UpdateLayout.
92+
void UpdateDpi(uint32_t dpi);
93+
8994
void UpdateLayout(const commands::CandidateWindow& candidates);
9095
void SetSendCommandInterface(
9196
client::SendCommandInterface* send_command_interface);
@@ -105,13 +110,6 @@ class InfolistWindow : public ATL::CWindowImpl<InfolistWindow, ATL::CWindow,
105110
OnDestroy();
106111
return 0;
107112
}
108-
inline LRESULT OnDpiChanged(UINT msg_id, WPARAM wparam, LPARAM lparam,
109-
BOOL& handled) {
110-
OnDpiChanged(static_cast<UINT>(LOWORD(wparam)),
111-
static_cast<UINT>(HIWORD(wparam)),
112-
reinterpret_cast<RECT*>(lparam));
113-
return 0;
114-
}
115113
inline LRESULT OnEraseBkgnd(UINT msg_id, WPARAM wparam, LPARAM lparam,
116114
BOOL& handled) {
117115
return static_cast<LRESULT>(OnEraseBkgnd(reinterpret_cast<HDC>(wparam)));
@@ -145,6 +143,7 @@ class InfolistWindow : public ATL::CWindowImpl<InfolistWindow, ATL::CWindow,
145143

146144
client::SendCommandInterface* send_command_interface_;
147145
std::unique_ptr<commands::CandidateWindow> candidate_window_;
146+
uint32_t dpi_;
148147
std::unique_ptr<TextRenderer> text_renderer_;
149148
std::unique_ptr<renderer::RendererStyle> style_;
150149
bool metrics_changed_;

0 commit comments

Comments
 (0)