xref: /reactos/win32ss/user/ntuser/caret.c (revision 9393fc32)
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.Pos.x = X;
221          ThreadQueue->CaretInfo.Pos.y = Y;
222          if (ThreadQueue->CaretInfo.Visible)
223          {
224             ThreadQueue->CaretInfo.Showing = 1;
225             co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
226          }
227 
228          IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
229          IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, pWnd, OBJID_CARET, CHILDID_SELF, 0);
230       }
231       return TRUE;
232    }
233 
234    return FALSE;
235 }
236 
237 BOOL FASTCALL co_UserHideCaret(PWND Window OPTIONAL)
238 {
239    PTHREADINFO pti;
240    PUSER_MESSAGE_QUEUE ThreadQueue;
241 
242    if (Window) ASSERT_REFS_CO(Window);
243 
244    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
245    {
246       EngSetLastError(ERROR_ACCESS_DENIED);
247       return FALSE;
248    }
249 
250    pti = PsGetCurrentThreadWin32Thread();
251    ThreadQueue = pti->MessageQueue;
252 
253    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
254    {
255       EngSetLastError(ERROR_ACCESS_DENIED);
256       return FALSE;
257    }
258 
259    if(ThreadQueue->CaretInfo.Visible)
260    {
261       PWND pwnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
262       IntKillTimer(pwnd, IDCARETTIMER, TRUE);
263 
264       co_IntHideCaret(&ThreadQueue->CaretInfo);
265       ThreadQueue->CaretInfo.Visible = 0;
266       ThreadQueue->CaretInfo.Showing = 0;
267    }
268 
269    return TRUE;
270 }
271 
272 BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL)
273 {
274    PTHREADINFO pti;
275    PUSER_MESSAGE_QUEUE ThreadQueue;
276    PWND pWnd = NULL;
277 
278    if (Window) ASSERT_REFS_CO(Window);
279 
280    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
281    {
282       EngSetLastError(ERROR_ACCESS_DENIED);
283       return FALSE;
284    }
285 
286    pti = PsGetCurrentThreadWin32Thread();
287    ThreadQueue = pti->MessageQueue;
288 
289    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
290    {
291       EngSetLastError(ERROR_ACCESS_DENIED);
292       return FALSE;
293    }
294 
295    if (!ThreadQueue->CaretInfo.Visible)
296    {
297       ThreadQueue->CaretInfo.Visible = 1;
298       pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
299       if (!ThreadQueue->CaretInfo.Showing && pWnd)
300       {
301          IntNotifyWinEvent(EVENT_OBJECT_SHOW, pWnd, OBJID_CARET, OBJID_CARET, 0);
302       }
303       IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
304    }
305    return TRUE;
306 }
307 
308 /* SYSCALLS *****************************************************************/
309 
310 BOOL
311 APIENTRY
312 NtUserCreateCaret(
313    HWND hWnd,
314    HBITMAP hBitmap,
315    int nWidth,
316    int nHeight)
317 {
318    PWND Window;
319    PTHREADINFO pti;
320    PUSER_MESSAGE_QUEUE ThreadQueue;
321    DECLARE_RETURN(BOOL);
322 
323    TRACE("Enter NtUserCreateCaret\n");
324    UserEnterExclusive();
325 
326    if(!(Window = UserGetWindowObject(hWnd)))
327    {
328       RETURN(FALSE);
329    }
330 
331    if(Window->head.pti->pEThread != PsGetCurrentThread())
332    {
333       EngSetLastError(ERROR_ACCESS_DENIED);
334       RETURN(FALSE);
335    }
336 
337    pti = PsGetCurrentThreadWin32Thread();
338    ThreadQueue = pti->MessageQueue;
339 
340    if (ThreadQueue->CaretInfo.Visible)
341    {
342       IntKillTimer(Window, IDCARETTIMER, TRUE);
343       co_IntHideCaret(&ThreadQueue->CaretInfo);
344    }
345 
346    ThreadQueue->CaretInfo.hWnd = hWnd;
347    if(hBitmap)
348    {
349       ThreadQueue->CaretInfo.Bitmap = hBitmap;
350       ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
351    }
352    else
353    {
354       if (nWidth == 0)
355       {
356           nWidth = UserGetSystemMetrics(SM_CXBORDER);
357       }
358       if (nHeight == 0)
359       {
360           nHeight = UserGetSystemMetrics(SM_CYBORDER);
361       }
362       ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
363       ThreadQueue->CaretInfo.Size.cx = nWidth;
364       ThreadQueue->CaretInfo.Size.cy = nHeight;
365    }
366    ThreadQueue->CaretInfo.Visible = 0;
367    ThreadQueue->CaretInfo.Showing = 0;
368 
369    IntSetTimer( Window, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM );
370 
371    IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_CARET, CHILDID_SELF, 0);
372 
373    RETURN(TRUE);
374 
375 CLEANUP:
376    TRACE("Leave NtUserCreateCaret, ret=%i\n",_ret_);
377    UserLeave();
378    END_CLEANUP;
379 }
380 
381 UINT
382 APIENTRY
383 NtUserGetCaretBlinkTime(VOID)
384 {
385    UINT ret;
386 
387    UserEnterShared();
388 
389    ret = gpsi->dtCaretBlink;
390 
391    UserLeave();
392 
393    return ret;
394 }
395 
396 BOOL
397 APIENTRY
398 NtUserGetCaretPos(
399    LPPOINT lpPoint)
400 {
401    PTHREADINFO pti;
402    PUSER_MESSAGE_QUEUE ThreadQueue;
403    NTSTATUS Status;
404    DECLARE_RETURN(BOOL);
405 
406    TRACE("Enter NtUserGetCaretPos\n");
407    UserEnterShared();
408 
409    pti = PsGetCurrentThreadWin32Thread();
410    ThreadQueue = pti->MessageQueue;
411 
412    Status = MmCopyToCaller(lpPoint, &ThreadQueue->CaretInfo.Pos, sizeof(POINT));
413    if(!NT_SUCCESS(Status))
414    {
415       SetLastNtError(Status);
416       RETURN(FALSE);
417    }
418 
419    RETURN(TRUE);
420 
421 CLEANUP:
422    TRACE("Leave NtUserGetCaretPos, ret=%i\n",_ret_);
423    UserLeave();
424    END_CLEANUP;
425 }
426 
427 BOOL
428 APIENTRY
429 NtUserShowCaret(HWND hWnd OPTIONAL)
430 {
431    PWND Window = NULL;
432    USER_REFERENCE_ENTRY Ref;
433    DECLARE_RETURN(BOOL);
434    BOOL ret;
435 
436    TRACE("Enter NtUserShowCaret\n");
437    UserEnterExclusive();
438 
439    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
440    {
441       RETURN(FALSE);
442    }
443 
444    if (Window) UserRefObjectCo(Window, &Ref);
445 
446    ret = co_UserShowCaret(Window);
447 
448    if (Window) UserDerefObjectCo(Window);
449 
450    RETURN(ret);
451 
452 CLEANUP:
453    TRACE("Leave NtUserShowCaret, ret=%i\n",_ret_);
454    UserLeave();
455    END_CLEANUP;
456 }
457 
458 BOOL
459 APIENTRY
460 NtUserHideCaret(HWND hWnd OPTIONAL)
461 {
462    PWND Window = NULL;
463    USER_REFERENCE_ENTRY Ref;
464    DECLARE_RETURN(BOOL);
465    BOOL ret;
466 
467    TRACE("Enter NtUserHideCaret\n");
468    UserEnterExclusive();
469 
470    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
471    {
472       RETURN(FALSE);
473    }
474 
475    if (Window) UserRefObjectCo(Window, &Ref);
476 
477    ret = co_UserHideCaret(Window);
478 
479    if (Window) UserDerefObjectCo(Window);
480 
481    RETURN(ret);
482 
483 CLEANUP:
484    TRACE("Leave NtUserHideCaret, ret=%i\n",_ret_);
485    UserLeave();
486    END_CLEANUP;
487 }
488