Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Windows.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
8#include "Stroika/Frameworks/Led/IdleManager.h"
9
10#include "Windows.h"
11
12using namespace Stroika::Foundation;
13
14CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led::Platform, qSupportWindowsSDKCallbacks, qSupportWindowsSDKCallbacks);
15#if qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug
16CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led::Platform, qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug, 1);
17#else
18CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led::Platform, qHookIMEEndCompositionMessageToWorkAroundWin2KIMEForNonUNICODEBug, 0);
19#endif
20
21namespace Stroika::Frameworks::Led::Platform {
22
23 namespace Private {
24 IdleMangerLinkerSupport::IdleMangerLinkerSupport ()
25 {
26 }
27 }
28
29 /*
30 @CLASS: IdleManagerOSImpl_Win32
31 @BASES: @'IdleManager::IdleManagerOSImpl'
32 @ACCESS: public
33 @DESCRIPTION: <p>Implemenation detail of the idle-task management system. This can generally be ignored by Led users.
34 </p>
35 */
36 class IdleManagerOSImpl_Win32 : public IdleManager::IdleManagerOSImpl {
37 public:
38 IdleManagerOSImpl_Win32 ();
39 ~IdleManagerOSImpl_Win32 ();
40
41 public:
42 virtual void StartSpendTimeCalls () override;
43 virtual void TerminateSpendTimeCalls () override;
44 virtual Foundation::Time::DurationSeconds GetSuggestedFrequency () const override;
45 virtual void SetSuggestedFrequency (Foundation::Time::DurationSeconds suggestedFrequency) override;
46
47 protected:
48 nonvirtual void OnTimer_Msg (UINT_PTR nEventID, TIMERPROC* proc);
49
50 protected:
51 nonvirtual void CheckAndCreateIdleWnd ();
52
53 public:
54 static LRESULT CALLBACK StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
55
56 private:
57 enum {
58 eTimerEventID = 34252
59 }; // Magic#
60 HWND fIdleWnd;
61 Foundation::Time::DurationSeconds fSuggestedFrequency;
62 UINT_PTR fTimerID;
63 };
64
65 namespace {
66 /*
67 * Code to automatically install and remove our idle manager.
68 */
69 struct IdleMangerSetter {
70 IdleMangerSetter ()
71 {
72 IdleManager::SetIdleManagerOSImpl (&fIdleManagerOSImpl);
73 }
74 ~IdleMangerSetter ()
75 {
76 IdleManager::SetIdleManagerOSImpl (NULL);
77 }
78 IdleManagerOSImpl_Win32 fIdleManagerOSImpl;
79 };
80 struct IdleMangerSetter sIdleMangerSetter;
81 }
82
83 /*
84 ********************************************************************************
85 ******************** FunnyMSPageUpDownAdjustSelectionHelper ********************
86 ********************************************************************************
87 */
88 FunnyMSPageUpDownAdjustSelectionHelper::FunnyMSPageUpDownAdjustSelectionHelper ()
89 : fRowNum (0)
90 {
91 }
92
93 void FunnyMSPageUpDownAdjustSelectionHelper::CaptureInfo (TextInteractor& ti)
94 {
95 size_t pinPoint = ti.GetSelectionStart ();
96 ptrdiff_t rowNum = ti.CalculateRowDeltaFromCharDeltaFromTopOfWindow (long (pinPoint) - long (ti.GetMarkerPositionOfStartOfWindow ()));
97 fRowNum = ::abs (rowNum);
98 }
99
100 void FunnyMSPageUpDownAdjustSelectionHelper::CompleteAdjustment (TextInteractor& ti)
101 {
102 // Finish the crazy caret adjustments!
103 size_t totalRowsInWindow = ti.GetTotalRowsInWindow (); // not can have changed from above due to scrolling
104 Assert (totalRowsInWindow >= 1);
105 fRowNum = min (fRowNum, totalRowsInWindow - 1);
106
107 size_t newRowStart = ti.CalculateCharDeltaFromRowDeltaFromTopOfWindow (fRowNum) + ti.GetMarkerPositionOfStartOfWindow ();
108 size_t newRowEnd = ti.GetEndOfRowContainingPosition (newRowStart);
109 Assert (newRowEnd >= newRowStart);
110
111 TextImager::GoalColumnRecomputerControlContext skipRecompute (ti, true);
112
113 size_t newSelStart = ti.GetRowRelativeCharAtLoc (TextImager::Tablet_Acquirer (&ti)->CvtFromTWIPSH (ti.GetSelectionGoalColumn ()), newRowStart);
114
115 newSelStart = min (newSelStart, newRowEnd); // pin to END of row
116
117 // Ha! Finally!
118 ti.SetSelection (newSelStart, newSelStart);
119 }
120
121 /*
122 ********************************************************************************
123 **************************** SimpleWin32WndProcHelper **************************
124 ********************************************************************************
125 */
126
127 /*
128 @METHOD: SimpleWin32WndProcHelper::Create
129 @DESCRIPTION: <p></p>
130 */
131 void SimpleWin32WndProcHelper::Create (DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
132 int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance)
133 {
134 SDKString tmpClassName;
135 if (lpClassName == NULL) {
136 tmpClassName = Characters::CString::Format (_T("LED_SimpleWin32WndProcHelper-%d-%p"), ::GetCurrentProcessId (), &StaticWndProc);
137 lpClassName = tmpClassName.c_str ();
138 {
139 static bool sRegistered = false;
140 if (not sRegistered) {
141 WNDCLASSEX wcex;
142 memset (&wcex, 0, sizeof (wcex));
143 wcex.cbSize = sizeof (WNDCLASSEX);
144 wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
145 wcex.lpszClassName = lpClassName;
146 ATOM regResult = ::RegisterClassEx (&wcex);
147 if (regResult == 0) {
148 DWORD laserErr = ::GetLastError ();
149 if (laserErr == ERROR_CLASS_ALREADY_EXISTS) {
150 // Shouldn't happen - but if it does - SB OK since StaticWndProc addr is the same!
151 }
152 else {
153 Assert (false); // could be a problem?
154 }
155 }
156 sRegistered = true;
157 }
158 }
159 }
160 [[maybe_unused]] HWND hWnd =
161 ::CreateWindowEx (dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, this);
162 Assert (hWnd == GetValidatedHWND ()); // already pre-set on the WM_CREATE message...
163 }
164
165 LRESULT CALLBACK SimpleWin32WndProcHelper::StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
166 {
167 if (message == WM_CREATE) {
168 LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam;
169 AssertNotNull (lpcs);
170 SimpleWin32WndProcHelper* pThis = reinterpret_cast<SimpleWin32WndProcHelper*> (lpcs->lpCreateParams);
171 Assert (pThis->GetHWND () == NULL); // cuz not set yet...
172 pThis->SetHWND (hWnd);
173 }
174
175 SimpleWin32WndProcHelper* pThis = reinterpret_cast<SimpleWin32WndProcHelper*> (::GetWindowLongPtr (hWnd, GWLP_USERDATA));
176
177 if (pThis == NULL) {
178 /*
179 * The only time I know of where this can happen is that these messages can be passed before the WM_CREATE.
180 * Seeing this assertion isn't necesarily a bug - but its a clue something wrong maybe going on.
181 */
182 Assert (message == WM_GETMINMAXINFO or message == WM_NCCREATE or message == WM_NCCALCSIZE);
183 return ::DefWindowProc (hWnd, message, wParam, lParam);
184 }
185
186 Assert (pThis != NULL);
187 Assert (pThis->GetHWND () == hWnd);
188 return pThis->WndProc (message, wParam, lParam);
189 }
190
191 /*
192 ********************************************************************************
193 **************************** IdleManagerOSImpl_Win32 ***************************
194 ********************************************************************************
195 */
196 IdleManagerOSImpl_Win32::IdleManagerOSImpl_Win32 ()
197 : fIdleWnd (NULL)
198 , fSuggestedFrequency (0)
199 , fTimerID (0)
200 {
201 }
202
203 IdleManagerOSImpl_Win32::~IdleManagerOSImpl_Win32 ()
204 {
205 if (fIdleWnd != NULL) {
206 ::DestroyWindow (fIdleWnd);
207 }
208 }
209
210 void IdleManagerOSImpl_Win32::StartSpendTimeCalls ()
211 {
212 CheckAndCreateIdleWnd ();
213 AssertNotNull (fIdleWnd);
214 // ignore if already started
215 if (fTimerID == 0) {
216 int timeout = static_cast<int> (fSuggestedFrequency.count () * 1000); // cvt specified frequency to milliseconds
217 Verify ((fTimerID = ::SetTimer (fIdleWnd, eTimerEventID, timeout, NULL)) != 0);
218 }
219 }
220
221 void IdleManagerOSImpl_Win32::TerminateSpendTimeCalls ()
222 {
223 if (fTimerID != 0) {
224 AssertNotNull (fIdleWnd);
225 Verify (::KillTimer (fIdleWnd, eTimerEventID));
226 fTimerID = 0;
227 }
228 }
229
230 Foundation::Time::DurationSeconds IdleManagerOSImpl_Win32::GetSuggestedFrequency () const
231 {
232 return fSuggestedFrequency;
233 }
234
235 void IdleManagerOSImpl_Win32::SetSuggestedFrequency (Foundation::Time::DurationSeconds suggestedFrequency)
236 {
237 if (fSuggestedFrequency != suggestedFrequency) {
238 fSuggestedFrequency = suggestedFrequency;
239 if (fTimerID != 0) {
240 TerminateSpendTimeCalls ();
241 StartSpendTimeCalls ();
242 }
243 }
244 }
245
246 LRESULT CALLBACK IdleManagerOSImpl_Win32::StaticWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
247 {
248 if (message == WM_CREATE) {
249 LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT> (lParam);
250 AssertNotNull (lpcs);
251 IdleManagerOSImpl_Win32* pThis = reinterpret_cast<IdleManagerOSImpl_Win32*> (lpcs->lpCreateParams);
252 ::SetWindowLongPtr (hWnd, GWLP_USERDATA, reinterpret_cast<DWORD_PTR> (pThis));
253 }
254
255 IdleManagerOSImpl_Win32* pThis = reinterpret_cast<IdleManagerOSImpl_Win32*> (::GetWindowLongPtr (hWnd, GWLP_USERDATA));
256
257 if (pThis == NULL) {
258 return ::DefWindowProc (hWnd, message, wParam, lParam);
259 }
260
261 Assert (pThis != NULL);
262 switch (message) {
263 case WM_TIMER:
264 pThis->OnTimer_Msg (wParam, reinterpret_cast<TIMERPROC*> (lParam));
265 return 0;
266 }
267 return ::DefWindowProc (hWnd, message, wParam, lParam);
268 }
269
270 void IdleManagerOSImpl_Win32::OnTimer_Msg (UINT_PTR /*nEventID*/, TIMERPROC* /*proc*/)
271 {
272/*
273 * Check if any input or paint messages pending, and if so - ignore the timer message as
274 * this isn't really IDLE time.
275 */
276#if defined(PM_QS_INPUT) || defined(PM_QS_PAINT)
277 MSG msg;
278 if (::PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE | PM_QS_INPUT | PM_QS_PAINT) == 0) {
279 CallSpendTime ();
280 }
281#else
282 //PM_QS_INPUT and PM_QS_PAINT not defined in MSVC60
283 CallSpendTime ();
284#endif
285 }
286
287 void IdleManagerOSImpl_Win32::CheckAndCreateIdleWnd ()
288 {
289 if (fIdleWnd == NULL) {
290 // Because of SPR#1549 - make sure the className depends on the ADDRESS of StaticWndProc
291 SDKString className = Characters::CString::Format (Led_SDK_TCHAROF ("Led::IdleManagerOSImpl_Win32 (%p)"), &StaticWndProc);
292 static SDKString sRegisteredClassName;
293 if (sRegisteredClassName != className) {
294 WNDCLASSEX wcex;
295 memset (&wcex, 0, sizeof (wcex));
296 wcex.cbSize = sizeof (WNDCLASSEX);
297 wcex.lpfnWndProc = (WNDPROC)StaticWndProc;
298 wcex.lpszClassName = className.c_str ();
299 ATOM regResult = ::RegisterClassEx (&wcex);
300 if (regResult == 0) {
301 DWORD x = ::GetLastError ();
302 if (x == ERROR_CLASS_ALREADY_EXISTS) {
303 // ignore - probably multiple template instantiations, or something like that. SB OK. They are all identical.
304 }
305 else {
306 Assert (false); // could be a problem?
307 }
308 }
309 sRegisteredClassName = className;
310 }
311 fIdleWnd = ::CreateWindowEx (0, className.c_str (), _T(""), 0, 0, 0, 1, 1, NULL, NULL, NULL, this);
312 }
313 }
314}
#define AssertNotNull(p)
Definition Assertions.h:333
#define Verify(c)
Definition Assertions.h:419
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
basic_string< SDKChar > SDKString
Definition SDKString.h:38