[WIN32SS][NTUSER] Fix caret position of Ghostscript (#1271)
[reactos.git] / win32ss / user / ntuser / caret.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Caret functions
5 * FILE: win32ss/user/ntuser/caret.c
6 * PROGRAMERS: Thomas Weidenmueller (w3seek@users.sourceforge.net)
7 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8 */
9
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserCaret);
12
13 /* DEFINES *****************************************************************/
14
15 #define MIN_CARETBLINKRATE 100
16 #define MAX_CARETBLINKRATE 10000
17
18 /* FUNCTIONS *****************************************************************/
19
20 VOID FASTCALL
21 co_IntDrawCaret(PWND pWnd, PTHRDCARETINFO CaretInfo)
22 {
23 HDC hdc, hdcMem;
24 HBITMAP hbmOld;
25 RECT rcClient;
26 BOOL bDone = FALSE;
27
28 if (pWnd == NULL)
29 {
30 TRACE("Null Window!\n");
31 return;
32 }
33
34 hdc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
35 if (!hdc)
36 {
37 ERR("GetDC failed\n");
38 return;
39 }
40
41 if (pWnd->hrgnUpdate)
42 {
43 NtGdiSaveDC(hdc);
44 }
45
46 IntGetClientRect(pWnd, &rcClient);
47 NtGdiIntersectClipRect(hdc,
48 rcClient.left,
49 rcClient.top,
50 rcClient.right,
51 rcClient.bottom);
52
53 if (CaretInfo->Bitmap)
54 {
55 if (!GreGetBitmapDimension(CaretInfo->Bitmap, &CaretInfo->Size))
56 {
57 ERR("Failed to get bitmap dimensions\n");
58 goto cleanup;
59 }
60
61 hdcMem = NtGdiCreateCompatibleDC(hdc);
62 if (hdcMem)
63 {
64 hbmOld = NtGdiSelectBitmap(hdcMem, CaretInfo->Bitmap);
65 bDone = NtGdiBitBlt(hdc,
66 CaretInfo->Pos.x,
67 CaretInfo->Pos.y,
68 CaretInfo->Size.cx,
69 CaretInfo->Size.cy,
70 hdcMem,
71 0,
72 0,
73 SRCINVERT,
74 0,
75 0);
76 NtGdiSelectBitmap(hdcMem, hbmOld);
77 GreDeleteObject(hdcMem);
78 }
79 }
80
81 if (!bDone)
82 {
83 NtGdiPatBlt(hdc,
84 CaretInfo->Pos.x,
85 CaretInfo->Pos.y,
86 CaretInfo->Size.cx,
87 CaretInfo->Size.cy,
88 DSTINVERT);
89 }
90
91 cleanup:
92 if (pWnd->hrgnUpdate)
93 {
94 NtGdiRestoreDC(hdc, -1);
95 }
96
97 UserReleaseDC(pWnd, hdc, FALSE);
98 }
99
100 VOID
101 CALLBACK
102 CaretSystemTimerProc(HWND hwnd,
103 UINT uMsg,
104 UINT_PTR idEvent,
105 DWORD dwTime)
106 {
107 PTHREADINFO pti;
108 PUSER_MESSAGE_QUEUE ThreadQueue;
109 PWND pWnd;
110
111 pti = PsGetCurrentThreadWin32Thread();
112 ThreadQueue = pti->MessageQueue;
113
114 if (ThreadQueue->CaretInfo.hWnd != hwnd)
115 {
116 TRACE("Not the same caret window!\n");
117 return;
118 }
119
120 if (hwnd)
121 {
122 pWnd = UserGetWindowObject(hwnd);
123 if (!pWnd)
124 {
125 ERR("Caret System Timer Proc has invalid window handle! %p Id: %u\n", hwnd, idEvent);
126 return;
127 }
128 }
129 else
130 {
131 TRACE( "Windowless Caret Timer Running!\n" );
132 return;
133 }
134
135 switch (idEvent)
136 {
137 case IDCARETTIMER:
138 {
139 ThreadQueue->CaretInfo.Showing = (ThreadQueue->CaretInfo.Showing ? 0 : 1);
140 co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
141 }
142 }
143 return;
144 }
145
146 static
147 BOOL FASTCALL
148 co_IntHideCaret(PTHRDCARETINFO CaretInfo)
149 {
150 PWND pWnd;
151 if(CaretInfo->hWnd && CaretInfo->Visible && CaretInfo->Showing)
152 {
153 pWnd = UserGetWindowObject(CaretInfo->hWnd);
154 CaretInfo->Showing = 0;
155
156 co_IntDrawCaret(pWnd, CaretInfo);
157 IntNotifyWinEvent(EVENT_OBJECT_HIDE, pWnd, OBJID_CARET, CHILDID_SELF, 0);
158 return TRUE;
159 }
160 return FALSE;
161 }
162
163 BOOL FASTCALL
164 co_IntDestroyCaret(PTHREADINFO Win32Thread)
165 {
166 PUSER_MESSAGE_QUEUE ThreadQueue;
167 PWND pWnd;
168 ThreadQueue = Win32Thread->MessageQueue;
169
170 if (!ThreadQueue)
171 return FALSE;
172
173 pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
174 co_IntHideCaret(&ThreadQueue->CaretInfo);
175 ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
176 ThreadQueue->CaretInfo.hWnd = (HWND)0;
177 ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
178 ThreadQueue->CaretInfo.Showing = 0;
179 ThreadQueue->CaretInfo.Visible = 0;
180 if (pWnd)
181 {
182 IntNotifyWinEvent(EVENT_OBJECT_DESTROY, pWnd, OBJID_CARET, CHILDID_SELF, 0);
183 }
184 return TRUE;
185 }
186
187 BOOL FASTCALL
188 IntSetCaretBlinkTime(UINT uMSeconds)
189 {
190 /* Don't save the new value to the registry! */
191
192 /* Windows doesn't do this check */
193 if((uMSeconds < MIN_CARETBLINKRATE) || (uMSeconds > MAX_CARETBLINKRATE))
194 {
195 EngSetLastError(ERROR_INVALID_PARAMETER);
196 return FALSE;
197 }
198
199 gpsi->dtCaretBlink = uMSeconds;
200
201 return TRUE;
202 }
203
204 BOOL FASTCALL
205 co_IntSetCaretPos(int X, int Y)
206 {
207 PTHREADINFO pti;
208 PWND pWnd;
209 PUSER_MESSAGE_QUEUE ThreadQueue;
210
211 pti = PsGetCurrentThreadWin32Thread();
212 ThreadQueue = pti->MessageQueue;
213
214 if(ThreadQueue->CaretInfo.hWnd)
215 {
216 pWnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
217 if(ThreadQueue->CaretInfo.Pos.x != X || ThreadQueue->CaretInfo.Pos.y != Y)
218 {
219 co_IntHideCaret(&ThreadQueue->CaretInfo);
220 ThreadQueue->CaretInfo.Showing = 1;
221 ThreadQueue->CaretInfo.Pos.x = X;
222 ThreadQueue->CaretInfo.Pos.y = Y;
223 co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
224
225 IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
226 IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, pWnd, OBJID_CARET, CHILDID_SELF, 0);
227 }
228 return TRUE;
229 }
230
231 return FALSE;
232 }
233
234 BOOL FASTCALL co_UserHideCaret(PWND Window OPTIONAL)
235 {
236 PTHREADINFO pti;
237 PUSER_MESSAGE_QUEUE ThreadQueue;
238
239 if (Window) ASSERT_REFS_CO(Window);
240
241 if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
242 {
243 EngSetLastError(ERROR_ACCESS_DENIED);
244 return FALSE;
245 }
246
247 pti = PsGetCurrentThreadWin32Thread();
248 ThreadQueue = pti->MessageQueue;
249
250 if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
251 {
252 EngSetLastError(ERROR_ACCESS_DENIED);
253 return FALSE;
254 }
255
256 if(ThreadQueue->CaretInfo.Visible)
257 {
258 PWND pwnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
259 IntKillTimer(pwnd, IDCARETTIMER, TRUE);
260
261 co_IntHideCaret(&ThreadQueue->CaretInfo);
262 ThreadQueue->CaretInfo.Visible = 0;
263 ThreadQueue->CaretInfo.Showing = 0;
264 }
265
266 return TRUE;
267 }
268
269 BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL)
270 {
271 PTHREADINFO pti;
272 PUSER_MESSAGE_QUEUE ThreadQueue;
273 PWND pWnd = NULL;
274
275 if (Window) ASSERT_REFS_CO(Window);
276
277 if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
278 {
279 EngSetLastError(ERROR_ACCESS_DENIED);
280 return FALSE;
281 }
282
283 pti = PsGetCurrentThreadWin32Thread();
284 ThreadQueue = pti->MessageQueue;
285
286 if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
287 {
288 EngSetLastError(ERROR_ACCESS_DENIED);
289 return FALSE;
290 }
291
292 if (!ThreadQueue->CaretInfo.Visible)
293 {
294 ThreadQueue->CaretInfo.Visible = 1;
295 pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
296 if (!ThreadQueue->CaretInfo.Showing && pWnd)
297 {
298 IntNotifyWinEvent(EVENT_OBJECT_SHOW, pWnd, OBJID_CARET, OBJID_CARET, 0);
299 }
300 IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
301 }
302 return TRUE;
303 }
304
305 /* SYSCALLS *****************************************************************/
306
307 BOOL
308 APIENTRY
309 NtUserCreateCaret(
310 HWND hWnd,
311 HBITMAP hBitmap,
312 int nWidth,
313 int nHeight)
314 {
315 PWND Window;
316 PTHREADINFO pti;
317 PUSER_MESSAGE_QUEUE ThreadQueue;
318 DECLARE_RETURN(BOOL);
319
320 TRACE("Enter NtUserCreateCaret\n");
321 UserEnterExclusive();
322
323 if(!(Window = UserGetWindowObject(hWnd)))
324 {
325 RETURN(FALSE);
326 }
327
328 if(Window->head.pti->pEThread != PsGetCurrentThread())
329 {
330 EngSetLastError(ERROR_ACCESS_DENIED);
331 RETURN(FALSE);
332 }
333
334 pti = PsGetCurrentThreadWin32Thread();
335 ThreadQueue = pti->MessageQueue;
336
337 if (ThreadQueue->CaretInfo.Visible)
338 {
339 IntKillTimer(Window, IDCARETTIMER, TRUE);
340 co_IntHideCaret(&ThreadQueue->CaretInfo);
341 }
342
343 ThreadQueue->CaretInfo.hWnd = hWnd;
344 if(hBitmap)
345 {
346 ThreadQueue->CaretInfo.Bitmap = hBitmap;
347 ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
348 }
349 else
350 {
351 if (nWidth == 0)
352 {
353 nWidth = UserGetSystemMetrics(SM_CXBORDER);
354 }
355 if (nHeight == 0)
356 {
357 nHeight = UserGetSystemMetrics(SM_CYBORDER);
358 }
359 ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
360 ThreadQueue->CaretInfo.Size.cx = nWidth;
361 ThreadQueue->CaretInfo.Size.cy = nHeight;
362 }
363 ThreadQueue->CaretInfo.Visible = 0;
364 ThreadQueue->CaretInfo.Showing = 0;
365
366 IntSetTimer( Window, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM );
367
368 IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_CARET, CHILDID_SELF, 0);
369
370 RETURN(TRUE);
371
372 CLEANUP:
373 TRACE("Leave NtUserCreateCaret, ret=%i\n",_ret_);
374 UserLeave();
375 END_CLEANUP;
376 }
377
378 UINT
379 APIENTRY
380 NtUserGetCaretBlinkTime(VOID)
381 {
382 UINT ret;
383
384 UserEnterShared();
385
386 ret = gpsi->dtCaretBlink;
387
388 UserLeave();
389
390 return ret;
391 }
392
393 BOOL
394 APIENTRY
395 NtUserGetCaretPos(
396 LPPOINT lpPoint)
397 {
398 PTHREADINFO pti;
399 PUSER_MESSAGE_QUEUE ThreadQueue;
400 NTSTATUS Status;
401 DECLARE_RETURN(BOOL);
402
403 TRACE("Enter NtUserGetCaretPos\n");
404 UserEnterShared();
405
406 pti = PsGetCurrentThreadWin32Thread();
407 ThreadQueue = pti->MessageQueue;
408
409 Status = MmCopyToCaller(lpPoint, &ThreadQueue->CaretInfo.Pos, sizeof(POINT));
410 if(!NT_SUCCESS(Status))
411 {
412 SetLastNtError(Status);
413 RETURN(FALSE);
414 }
415
416 RETURN(TRUE);
417
418 CLEANUP:
419 TRACE("Leave NtUserGetCaretPos, ret=%i\n",_ret_);
420 UserLeave();
421 END_CLEANUP;
422 }
423
424 BOOL
425 APIENTRY
426 NtUserShowCaret(HWND hWnd OPTIONAL)
427 {
428 PWND Window = NULL;
429 USER_REFERENCE_ENTRY Ref;
430 DECLARE_RETURN(BOOL);
431 BOOL ret;
432
433 TRACE("Enter NtUserShowCaret\n");
434 UserEnterExclusive();
435
436 if(hWnd && !(Window = UserGetWindowObject(hWnd)))
437 {
438 RETURN(FALSE);
439 }
440
441 if (Window) UserRefObjectCo(Window, &Ref);
442
443 ret = co_UserShowCaret(Window);
444
445 if (Window) UserDerefObjectCo(Window);
446
447 RETURN(ret);
448
449 CLEANUP:
450 TRACE("Leave NtUserShowCaret, ret=%i\n",_ret_);
451 UserLeave();
452 END_CLEANUP;
453 }
454
455 BOOL
456 APIENTRY
457 NtUserHideCaret(HWND hWnd OPTIONAL)
458 {
459 PWND Window = NULL;
460 USER_REFERENCE_ENTRY Ref;
461 DECLARE_RETURN(BOOL);
462 BOOL ret;
463
464 TRACE("Enter NtUserHideCaret\n");
465 UserEnterExclusive();
466
467 if(hWnd && !(Window = UserGetWindowObject(hWnd)))
468 {
469 RETURN(FALSE);
470 }
471
472 if (Window) UserRefObjectCo(Window, &Ref);
473
474 ret = co_UserHideCaret(Window);
475
476 if (Window) UserDerefObjectCo(Window);
477
478 RETURN(ret);
479
480 CLEANUP:
481 TRACE("Leave NtUserHideCaret, ret=%i\n",_ret_);
482 UserLeave();
483 END_CLEANUP;
484 }